public InitConfig(MediaKind mediaKind, TransceiverInitSettings settings)
 {
     name             = settings?.Name;
     this.mediaKind   = mediaKind;
     desiredDirection = (settings != null ? settings.InitialDesiredDirection : new TransceiverInitSettings().InitialDesiredDirection);
     encodedStreamIds = Utils.EncodeTransceiverStreamIDs(settings?.StreamIDs);
 }
Exemple #2
0
        public bool this[MediaKind kind]
        {
            get
            {
                var ret = kind switch
                {
                    MediaKind.Audio => Audio,
                    MediaKind.Video => Video,
                    _ => false,
                };
                return(ret);
            }
            set
            {
                switch (kind)
                {
                case MediaKind.Audio:
                    Audio = value;
                    break;

                case MediaKind.Video:
                    Video = value;
                    break;
                }
            }
        }
Exemple #3
0
        /// <summary>
        /// <para>Events:</para>
        /// <para>@emits transportclose</para></para>
        /// <para>@emits score - (score: ProducerScore[])</para>
        /// <para>@emits videoorientationchange - (videoOrientation: ProducerVideoOrientation)</para>
        /// <para>@emits trace - (trace: ProducerTraceEventData)</para>
        /// <para>@emits @close</para>
        /// <para>Observer events:</para>
        /// <para>@emits close</para>
        /// <para>@emits pause</para>
        /// <para>@emits resume</para>
        /// <para>@emits score - (score: ProducerScore[])</para>
        /// <para>@emits videoorientationchange - (videoOrientation: ProducerVideoOrientation)</para>
        /// <para>@emits trace - (trace: ProducerTraceEventData)</para>
        /// </summary>
        /// <param name="loggerFactory"></param>
        /// <param name="producerInternalData"></param>
        /// <param name="kind"></param>
        /// <param name="rtpParameters"></param>
        /// <param name="type"></param>
        /// <param name="consumableRtpParameters"></param>
        /// <param name="channel"></param>
        /// <param name="appData"></param>
        /// <param name="paused"></param>
        public Producer(ILoggerFactory loggerFactory,
                        ProducerInternalData producerInternalData,
                        MediaKind kind,
                        RtpParameters rtpParameters,
                        ProducerType type,
                        RtpParameters consumableRtpParameters,
                        Channel channel,
                        Dictionary <string, object>?appData,
                        bool paused)
        {
            _logger = loggerFactory.CreateLogger <Producer>();

            // Internal
            Internal = producerInternalData;

            // Data
            Kind                    = kind;
            RtpParameters           = rtpParameters;
            Type                    = type;
            ConsumableRtpParameters = consumableRtpParameters;

            _channel = channel;
            AppData  = appData;
            Paused   = paused;

            HandleWorkerNotifications();
        }
        /// <summary>
        /// <para>Events:</para>
        /// <para>@emits transportclose</para></para>
        /// <para>@emits score - (score: ProducerScore[])</para>
        /// <para>@emits videoorientationchange - (videoOrientation: ProducerVideoOrientation)</para>
        /// <para>@emits trace - (trace: ProducerTraceEventData)</para>
        /// <para>@emits @close</para>
        /// <para>Observer events:</para>
        /// <para>@emits close</para>
        /// <para>@emits pause</para>
        /// <para>@emits resume</para>
        /// <para>@emits score - (score: ProducerScore[])</para>
        /// <para>@emits videoorientationchange - (videoOrientation: ProducerVideoOrientation)</para>
        /// <para>@emits trace - (trace: ProducerTraceEventData)</para>
        /// </summary>
        /// <param name="loggerFactory"></param>
        /// <param name="producerInternalData"></param>
        /// <param name="kind"></param>
        /// <param name="rtpParameters"></param>
        /// <param name="type"></param>
        /// <param name="consumableRtpParameters"></param>
        /// <param name="channel"></param>
        /// <param name="appData"></param>
        /// <param name="paused"></param>
        public Producer(ILoggerFactory loggerFactory,
                        ProducerInternalData producerInternalData,
                        MediaKind kind,
                        RtpParameters rtpParameters,
                        ProducerType type,
                        RtpParameters consumableRtpParameters,
                        Channel channel,
                        PayloadChannel payloadChannel,
                        Dictionary <string, object>?appData,
                        bool paused
                        )
        {
            _logger = loggerFactory.CreateLogger <Producer>();

            // Internal
            _internal = producerInternalData;

            // Data
            Kind                    = kind;
            RtpParameters           = rtpParameters;
            Type                    = type;
            ConsumableRtpParameters = consumableRtpParameters;

            _channel        = channel;
            _payloadChannel = payloadChannel;
            AppData         = appData;
            _paused         = paused;

            _checkConsumersTimer = new Timer(CheckConsumers, null, TimeSpan.FromSeconds(CheckConsumersTimeSeconds), TimeSpan.FromMilliseconds(-1));

            HandleWorkerNotifications();
        }
Exemple #5
0
        /// <summary>
        /// <para>Events:</para>
        /// <para>@emits transportclose</para>
        /// <para>@emits producerclose</para>
        /// <para>@emits producerpause</para>
        /// <para>@emits producerresume</para>
        /// <para>@emits score - (score: ConsumerScore)</para>
        /// <para>@emits layerschange - (layers: ConsumerLayers | undefined)</para>
        /// <para>@emits trace - (trace: ConsumerTraceEventData)</para>
        /// <para>@emits @close</para>
        /// <para>@emits @producerclose</para>
        /// <para>Observer events:</para>
        /// <para>@emits close</para>
        /// <para>@emits pause</para>
        /// <para>@emits resume</para>
        /// <para>@emits score - (score: ConsumerScore)</para>
        /// <para>@emits layerschange - (layers: ConsumerLayers | undefined)</para>
        /// <para>@emits rtp - (packet: Buffer)</para>
        /// <para>@emits trace - (trace: ConsumerTraceEventData)</para>
        /// </summary>
        /// <param name="loggerFactory"></param>
        /// <param name="consumerInternalData"></param>
        /// <param name="kind"></param>
        /// <param name="rtpParameters"></param>
        /// <param name="type"></param>
        /// <param name="channel"></param>
        /// <param name="appData"></param>
        /// <param name="paused"></param>
        /// <param name="producerPaused"></param>
        /// <param name="score"></param>
        /// <param name="preferredLayers"></param>
        public Consumer(ILoggerFactory loggerFactory,
                        ConsumerInternalData consumerInternalData,
                        MediaKind kind,
                        RtpParameters rtpParameters,
                        ConsumerType type,
                        Channel channel,
                        PayloadChannel payloadChannel,
                        Dictionary <string, object>?appData,
                        bool paused,
                        bool producerPaused,
                        ConsumerScore?score,
                        ConsumerLayers?preferredLayers
                        )
        {
            _logger = loggerFactory.CreateLogger <Consumer>();

            // Internal
            _internal = consumerInternalData;

            // Data
            Kind          = kind;
            RtpParameters = rtpParameters;
            Type          = type;

            _channel        = channel;
            _payloadChannel = payloadChannel;
            AppData         = appData;
            _paused         = paused;
            ProducerPaused  = producerPaused;
            Score           = score;
            PreferredLayers = preferredLayers;

            HandleWorkerNotifications();
        }
Exemple #6
0
        /// <summary>
        /// Add a new media line of the given kind.
        ///
        /// This method creates a media line, which expresses an intent from the user to get a transceiver.
        /// The actual <see xref="WebRTC.Transceiver"/> object creation is delayed until a session
        /// negotiation is completed.
        ///
        /// Once the media line is created, the user can then assign its <see cref="MediaLine.Source"/> and
        /// <see cref="MediaLine.Receiver"/> properties to express their intent to send and/or receive some media
        /// through the transceiver that will be associated with that media line once a session is negotiated.
        /// This information is used in subsequent negotiations to derive a
        /// <see xref="Microsoft.MixedReality.WebRTC.Transceiver.Direction"/> to negotiate. Therefore users
        /// should avoid modifying the <see cref="Transceiver.DesiredDirection"/> property manually when using
        /// the Unity library, and instead modify the <see cref="MediaLine.Source"/> and
        /// <see cref="MediaLine.Receiver"/> properties.
        /// </summary>
        /// <param name="kind">The kind of media (audio or video) for the transceiver.</param>
        /// <returns>A newly created media line, which will be associated with a transceiver once the next session
        /// is negotiated.</returns>
        public MediaLine AddMediaLine(MediaKind kind)
        {
            var ml = new MediaLine(kind);

            _mediaLines.Add(ml);
            return(ml);
        }
Exemple #7
0
    void OnCollisionEnter(Collision collision)
    {
        if (collision.gameObject.CompareTag("Condition"))
        {
            ConditionActionK colK = collision.gameObject.GetComponent <ConditionAction>().MyKind;
            gameObject.GetComponent <MediaKind>().MyKind = colK;
            gameObject.tag = "ConditionMedia";
        }
        else if (collision.gameObject.CompareTag("Action"))
        {
            ConditionActionK colK = collision.gameObject.GetComponent <ConditionAction>().MyKind;
            gameObject.GetComponent <MediaKind>().MyKind = colK;
            gameObject.tag = "ActionMedia";
        }

        /*else if (collision.gameObject.CompareTag("Initial"))
         * {
         *  MediaKind Inst = gameObject.GetComponent<MediaKind>();
         *  Inst.IsInitialMedia = !Inst.IsInitialMedia;
         *  if (Inst.IsInitialMedia) {
         *      controller.OnEntry(gameObject);
         *      text.text = "Port " + Inst.MediaId;
         *  }
         *  else {
         *      controller.RemoveEntry(gameObject);
         *      text.text = "Port Removed "+ Inst.MediaId;
         *  }
         * }*/
        else if (collision.gameObject.CompareTag("Initial"))
        {
            MediaKind Inst = gameObject.GetComponent <MediaKind>();
            if (!Inst.IsInitialMedia)
            {
                Inst.IsInitialMedia = true;
                controller.OnEntry(gameObject);
                text.text = "Port " + Inst.MediaId;
            }
            else
            {
                Inst.IsInitialMedia = false;
                controller.RemoveEntry(gameObject);
            }
        }
        else if (gameObject.CompareTag("ConditionMedia"))
        {
            MediaKind Inst    = gameObject.GetComponent <MediaKind>();
            MediaKind colInst = collision.gameObject.GetComponent <MediaKind>();
            if (collision.gameObject.CompareTag("ActionMedia"))
            {
                /*Debug.Log(
                 * Enum.GetName(typeof(ConditionActionK), Inst.MyKind) + " " + Inst.MediaId + " " +
                 * Enum.GetName(typeof(ConditionActionK), colInst.MyKind) + " " + colInst.MediaId + " "
                 * );*/
                text.text = Enum.GetName(typeof(ConditionActionK), Inst.MyKind) + " " + Inst.MediaId + " " +
                            Enum.GetName(typeof(ConditionActionK), colInst.MyKind) + " " + colInst.MediaId + " ";
                controller.CreateLink(gameObject, collision.gameObject);
            }
        }
    }
        public TransceiverMetadata(string mid, MediaQuality quality, MediaKind kind, Guid sourceDeviceId)
        {
            Require.NotNullOrWhiteSpace(mid);
            Require.NotEmpty(sourceDeviceId);

            TransceiverMid = mid.Trim();
            Quality        = quality;
            Kind           = kind;
            SourceDeviceId = sourceDeviceId;
        }
Exemple #9
0
        private void AddPendingTransceiver(MediaKind mediaKind, string name)
        {
            var settings = new TransceiverInitSettings
            {
                Name = name,
                InitialDesiredDirection = Transceiver.Direction.SendReceive,
            };

            SessionModel.Current.AddTransceiver(mediaKind, settings);
        }
Exemple #10
0
        public Task <IList <ICodec> > GetCodecsAsync(MediaKind kind)
        {
            IList <ICodec> videoCodecsList = new List <ICodec>();
            IList <ICodec> audioCodecsList = new List <ICodec>();

            if (kind == MediaKind.AudioCodec)
            {
                return(Task.Run(() =>
                {
                    RTCRtpCapabilities audioCapabilities = RTCRtpSender.GetCapabilities(new WebRtcFactory(new WebRtcFactoryConfiguration()), "audio");
                    IReadOnlyList <RTCRtpCodecCapability> audioCodecs = audioCapabilities.Codecs;
                    foreach (var item in audioCodecs)
                    {
                        string payload = item.PreferredPayloadType.ToString();

                        Codec audioCodec = new Codec();
                        audioCodec.SetMediaKind(MediaKind.AudioCodec);
                        audioCodec.SetId(payload);
                        audioCodec.SetDisplayName(item.Name + " " + payload);
                        audioCodec.SetRate((int)item.ClockRate);

                        audioCodecsList.Add(audioCodec);
                    }
                    return audioCodecsList;
                }));
            }

            if (kind == MediaKind.VideoCodec)
            {
                return(Task.Run(() =>
                {
                    RTCRtpCapabilities videoCapabilities = RTCRtpSender.GetCapabilities(new WebRtcFactory(new WebRtcFactoryConfiguration()), "video");
                    IReadOnlyList <RTCRtpCodecCapability> videoCodecs = videoCapabilities.Codecs;
                    foreach (var item in videoCodecs)
                    {
                        string payload = item.PreferredPayloadType.ToString();

                        Codec videoCodec = new Codec();
                        videoCodec.SetMediaKind(MediaKind.VideoCodec);
                        videoCodec.SetId(payload);
                        videoCodec.SetDisplayName(item.Name + " " + payload);
                        videoCodec.SetRate((int)item.ClockRate);

                        videoCodecsList.Add(videoCodec);
                    }

                    return videoCodecsList;
                }));
            }
            else
            {
                return(null);
            }
        }
Exemple #11
0
 /// <summary>
 /// Create a new transceiver associated with a given peer connection.
 /// </summary>
 /// <param name="handle">Handle to the native transceiver object.</param>
 /// <param name="mediaKind">The media kind of the transceiver and its tracks.</param>
 /// <param name="peerConnection">The peer connection owning this transceiver.</param>
 /// <param name="mlineIndex">The transceiver media line index in SDP.</param>
 /// <param name="name">The transceiver name.</param>
 /// <param name="streamIDs">Collection of stream IDs the transceiver is associated with, as set by the peer which created it.</param>
 /// <param name="initialDesiredDirection">Initial value to initialize <see cref="DesiredDirection"/> with.</param>
 internal Transceiver(TransceiverInterop.TransceiverHandle handle, MediaKind mediaKind, PeerConnection peerConnection, int mlineIndex,
                      string name, string[] streamIDs, Direction initialDesiredDirection)
 {
     Debug.Assert(!handle.IsClosed);
     _nativeHandle     = handle;
     MediaKind         = mediaKind;
     PeerConnection    = peerConnection;
     MlineIndex        = mlineIndex;
     Name              = name;
     StreamIDs         = streamIDs;
     _desiredDirection = initialDesiredDirection;
     TransceiverInterop.RegisterCallbacks(this, out _argsRef);
 }
Exemple #12
0
        /// <summary>
        /// Gets the object item kind
        /// </summary>
        public static string GetObjectItem(MediaKind mediaKind)
        {
            switch (mediaKind)
            {
            case MediaKind.Video:
                return("videoItem.movie");

            case MediaKind.Image:
                return("imageItem.photo");

            case MediaKind.Audio:
                return("audioItem.musicTrack");

            default:
                return("textItem");
            }
        }
        public void RefreshSenderList(MediaKind mediaKind, bool force = false)
        {
            if ((mediaKind != _availableMediaKind) || force)
            {
                _availableMediaKind = mediaKind;

                // Rebuild the list of sender tracks for the given media kind
                var senders = new ObservableCollection <SenderTrackViewModel>();
                senders.Add(NullSenderTrack);
                if (_availableMediaKind == MediaKind.Audio)
                {
                    foreach (var trackViewModel in SessionModel.Current.AudioTracks)
                    {
                        if (trackViewModel.IsRemote)
                        {
                            continue;
                        }
                        var track = trackViewModel?.TrackImpl;
                        if (track != null)
                        {
                            senders.Add(new SenderTrackViewModel(track));
                        }
                    }
                }
                else if (_availableMediaKind == MediaKind.Video)
                {
                    foreach (var trackViewModel in SessionModel.Current.VideoTracks)
                    {
                        if (trackViewModel.IsRemote)
                        {
                            continue;
                        }
                        var track = trackViewModel?.TrackImpl;
                        if (track != null)
                        {
                            senders.Add(new SenderTrackViewModel(track));
                        }
                    }
                }
                AvailableSenders = senders; // FIXME - this is not thread-aware, and RefreshSenderList() can be called from non-UI thread...
                SelectedItem?.NotifySenderChanged();
            }
        }
Exemple #14
0
        public RtpTransceiver AddTransceiver(MediaKind kind, RtpTransceiverDirection direction)
        {
            SafetyCheck();
            if (kind == MediaKind.Data)
            {
                throw new NotSupportedException();
            }

            var transceiverPtr = PeerConnectionInterop.AddTransceiver(_handle, kind == MediaKind.Audio, direction);

            if (transceiverPtr == IntPtr.Zero)
            {
                throw new AddTransceiverFailedException();
            }

            var tmp = new RtpTransceiver(transceiverPtr, _signallingThread);

            _knownTransceivers.Add(transceiverPtr, tmp);
            return(tmp);
        }
        void OnEnable()
        {
            autoInitOnStart_ = serializedObject.FindProperty("AutoInitializeOnStart");
            autoCreateOffer_ = serializedObject.FindProperty("AutoCreateOfferOnRenegotiationNeeded");
            autoLogErrors_   = serializedObject.FindProperty("AutoLogErrorsToUnityConsole");

            iceServers_    = serializedObject.FindProperty("IceServers");
            iceUsername_   = serializedObject.FindProperty("IceUsername");
            iceCredential_ = serializedObject.FindProperty("IceCredential");

            onInitialized_ = serializedObject.FindProperty("OnInitialized");
            onShutdown_    = serializedObject.FindProperty("OnShutdown");
            onError_       = serializedObject.FindProperty("OnError");

            mediaLines_      = serializedObject.FindProperty("_mediaLines");
            transceiverList_ = new ReorderableList(serializedObject, mediaLines_, true, true, true, true);
            transceiverList_.elementHeight       = 2 * kLineHeight + kItemSpacing;
            transceiverList_.drawHeaderCallback  = (Rect rect) => EditorGUI.LabelField(rect, "Transceivers");
            transceiverList_.drawElementCallback =
                (Rect rect, int index, bool isActive, bool isFocused) =>
            {
                var element = transceiverList_.serializedProperty.GetArrayElementAtIndex(index);

                float x0 = rect.x;
                float x1 = x0 + 16;
                float y0 = rect.y + 2;
                float y1 = y0 + kLineHeight;

                // MID value
                EditorGUI.LabelField(new Rect(x0, y0, 20, 20), $"{index}");

                // Audio or video icon for transceiver kind
                MediaKind   kind = (MediaKind)element.FindPropertyRelative("_kind").intValue;
                System.Type senderType, receiverType;
                if (kind == MediaKind.Audio)
                {
                    senderType   = typeof(AudioSender);
                    receiverType = typeof(AudioReceiver);
                    DrawSpriteIcon(IconType.Audio, new Rect(x1, rect.y, 20, 20));
                }
                else
                {
                    senderType   = typeof(VideoSender);
                    receiverType = typeof(VideoReceiver);
                    DrawSpriteIcon(IconType.Video, new Rect(x1, rect.y, 20, 20));
                }

                rect.x     += kIconSpacing;
                rect.width -= kIconSpacing;

                rect.x     += 18;
                rect.width -= 18;

                float fieldWidth  = rect.width;
                bool  hasSender   = false;
                bool  hasReceiver = false;
                {
                    var    p   = element.FindPropertyRelative("_sender");
                    Object obj = p.objectReferenceValue;
                    obj = EditorGUI.ObjectField(
                        new Rect(rect.x, y0, fieldWidth, EditorGUIUtility.singleLineHeight),
                        obj, senderType, true);
                    hasSender = (obj != null);
                    p.objectReferenceValue = obj;
                }
                {
                    var    p   = element.FindPropertyRelative("_receiver");
                    Object obj = p.objectReferenceValue;
                    obj = EditorGUI.ObjectField(
                        new Rect(rect.x, y1, fieldWidth, EditorGUIUtility.singleLineHeight),
                        obj, receiverType, true);
                    hasReceiver            = (obj != null);
                    p.objectReferenceValue = obj;
                }

                IconType iconType = IconType.Inactive;
                if (hasSender)
                {
                    if (hasReceiver)
                    {
                        iconType = IconType.SendRecv;
                    }
                    else
                    {
                        iconType = IconType.SendOnly;
                    }
                }
                else if (hasReceiver)
                {
                    iconType = IconType.RecvOnly;
                }
                DrawSpriteIcon(iconType, new Rect(x0 + 8, y1, 16, 16));
            };
            transceiverList_.drawNoneElementCallback = (Rect rect) =>
            {
                GUIStyle style = new GUIStyle(EditorStyles.label);
                style.alignment = TextAnchor.MiddleCenter;
                EditorGUI.LabelField(rect, "(empty)", style);
            };
            transceiverList_.displayAdd = false;
        }
Exemple #16
0
 public void SetMediaKind(MediaKind mediaKind)
 {
     Kind = mediaKind;
 }
Exemple #17
0
        //private static string noClue;
        public static void ReadFile(MP4File file)
        {
            try
            {
                title = file.Tags.Title;
            }
            catch
            {
                title = "";
            }

            try
            {
                artist = file.Tags.Artist;
            }
            catch
            {
                artist = "";
            }

            try
            {
                releaseDate = file.Tags.ReleaseDate;
            }
            catch
            {
                releaseDate = "";
            }

            try
            {
                rating = (string)file.Tags.RatingInfo.Rating;
            }
            catch
            {
                rating = "";
            }

            try
            {
                genre = file.Tags.Genre;
            }
            catch
            {
                genre = "";
            }

            try
            {
                artwork = file.Tags.Artwork;
            }
            catch
            {
                artwork = null;
            }

            try
            {
                album = file.Tags.Album;
            }
            catch
            {
                album = "";
            }

            try
            {
                albumArtist = file.Tags.AlbumArtist;
            }
            catch
            {
                albumArtist = "";
            }

            try
            {
                purchaseDate = file.Tags.PurchasedDate;
            }
            catch
            {
                purchaseDate = "";
            }

            try
            {
                shortDescription = file.Tags.Description;
            }
            catch
            {
                shortDescription = "";
            }

            try
            {
                longDescription = file.Tags.LongDescription;
            }
            catch
            {
                longDescription = "";
            }

            try
            {
                videoKind = file.Tags.MediaType;
            }
            catch
            {
                videoKind = 0;
            }

            try
            {
                if (file.Tags.MovieInfo.HasCast)
                {
                    actors = String.Join(",", file.Tags.MovieInfo.Cast);
                }
                else
                {
                    actors = "";
                }
            }
            catch
            {
                actors = "";
            }

            try
            {
                if (file.Tags.MovieInfo.HasDirectors)
                {
                    director = String.Join(",", file.Tags.MovieInfo.Directors);
                }
                else
                {
                    director = "";
                }
            }
            catch
            {
                director = "";
            }

            try
            {
                if (file.Tags.MovieInfo.HasScreenwriters)
                {
                    screenwriter = String.Join(",", file.Tags.MovieInfo.Screenwriters);
                }
                else
                {
                    screenwriter = "";
                }
            }
            catch
            {
                screenwriter = "";
            }

            try
            {
                show = file.Tags.TVShow;
            }
            catch
            {
                show = "";
            }

            try
            {
                episodeId = file.Tags.EpisodeId;
            }
            catch
            {
                episodeId = "";
            }

            try
            {
                season = (int)file.Tags.SeasonNumber;
            }
            catch
            {
                season = 0;
            }

            try
            {
                episode = (int)file.Tags.EpisodeNumber;
            }
            catch
            {
                episode = 0;
            }

            try
            {
                tvNetwork = file.Tags.TVNetwork;
            }
            catch
            {
                tvNetwork = "";
            }

            try
            {
                isPodcast = (Boolean)file.Tags.IsPodcast;
            }
            catch
            {
                isPodcast = false;
            }

            feedUrl = "";
            episodeUrl = "";

            try
            {
                category = file.Tags.Category;
            }
            catch
            {
                category = "";
            }

            try
            {
                keyword = file.Tags.Keywords;
            }
            catch
            {
                keyword = "";
            }

            try
            {
                advisory = file.Tags.ContentRating;
            }
            catch
            {
                advisory = 0;
            }
        }
Exemple #18
0
        public RtpParameters GetSendingRtpParameters(MediaKind kind, ExtendedRtpCapabilities extendedRtpCapabilities)
        {
            List <RtpCodecParameters> codecs = new();

            foreach (var extendedCodec in extendedRtpCapabilities.Codecs)
            {
                if (extendedCodec.Kind != kind)
                {
                    continue;
                }

                RtpCodecParameters codec = new()
                {
                    MimeType     = extendedCodec.MimeType,
                    PayloadType  = extendedCodec.LocalPayloadType,
                    ClockRate    = extendedCodec.ClockRate,
                    Channels     = extendedCodec.Channels,
                    Parameters   = extendedCodec.LocalParameters,
                    RtcpFeedback = extendedCodec.RtcpFeedback
                };
                codecs.Add(codec);

                if (extendedCodec.LocalRtxPayloadType.HasValue)
                {
                    RtpCodecParameters rtxCodec = new()
                    {
                        MimeType    = $"{extendedCodec.Kind.DisplayName()}/rtx",
                        PayloadType = (int)extendedCodec.LocalRtxPayloadType,
                        ClockRate   = extendedCodec.ClockRate,
                        Parameters  = new Dictionary <string, object>
                        {
                            { "apt", extendedCodec.LocalPayloadType }
                        },
                        RtcpFeedback = new RtcpFeedback[] { }
                    };
                    codecs.Add(rtxCodec);
                }
            }

            List <RtpHeaderExtensionParameters> headerExtensions = new();

            foreach (var extendedExtendion in extendedRtpCapabilities.HeaderExtensions)
            {
                // Ignore RTP extensions of a different kind and those not valid for sending.
                if ((extendedExtendion.Kind != kind) ||
                    (extendedExtendion.Direction != Direction.SendRecv &&
                     extendedExtendion.Direction != Direction.SendOnly))
                {
                    continue;
                }

                RtpHeaderExtensionParameters ext = new()
                {
                    Uri        = extendedExtendion.Uri,
                    Id         = extendedExtendion.SendId,
                    Encrypt    = (bool)extendedExtendion.PreferredEncrypt,
                    Parameters = new()
                };
                headerExtensions.Add(ext);
            }

            return(new RtpParameters
            {
                Codecs = codecs.ToArray(),
                HeaderExtensions = headerExtensions.ToArray(),
                Encodings = new RtpEncodingParameters[] { },
                Rtcp = new()
            });
        }
Exemple #19
0
        public RtpParameters GetSendingRemoteRtpParameters(MediaKind kind,
                                                           ExtendedRtpCapabilities extendedRtpCapabilities)
        {
            List <RtpCodecParameters> codecs = new();

            foreach (var extendedCodec in extendedRtpCapabilities.Codecs)
            {
                if (extendedCodec.Kind != kind)
                {
                    continue;
                }

                RtpCodecParameters codec = new()
                {
                    MimeType     = extendedCodec.MimeType,
                    PayloadType  = extendedCodec.LocalPayloadType,
                    ClockRate    = extendedCodec.ClockRate,
                    Channels     = extendedCodec.Channels,
                    Parameters   = extendedCodec.RemoteParameters,
                    RtcpFeedback = extendedCodec.RtcpFeedback
                };
                codecs.Add(codec);

                if (extendedCodec.LocalRtxPayloadType.HasValue)
                {
                    RtpCodecParameters rtxCodec = new()
                    {
                        MimeType    = $"{extendedCodec.Kind.DisplayName()}/rtx",
                        PayloadType = (int)extendedCodec.LocalRtxPayloadType,
                        ClockRate   = extendedCodec.ClockRate,
                        Parameters  = new Dictionary <string, object>
                        {
                            { "apt", extendedCodec.LocalPayloadType }
                        },
                        RtcpFeedback = new RtcpFeedback[] { }
                    };
                    codecs.Add(rtxCodec);
                }
            }

            List <RtpHeaderExtensionParameters> headerExtensions = new();

            foreach (var extendedExtendion in extendedRtpCapabilities.HeaderExtensions)
            {
                // Ignore RTP extensions of a different kind and those not valid for sending.
                if ((extendedExtendion.Kind != kind) ||
                    (extendedExtendion.Direction != Direction.SendRecv &&
                     extendedExtendion.Direction != Direction.SendOnly))
                {
                    continue;
                }

                RtpHeaderExtensionParameters ext = new()
                {
                    Uri     = extendedExtendion.Uri,
                    Id      = extendedExtendion.SendId,
                    Encrypt = (bool)extendedExtendion.PreferredEncrypt,
                };
                headerExtensions.Add(ext);
            }

            // Reduce codecs' RTCP feedback. Use Transport-CC if available, REMB otherwise.
            if (headerExtensions.Any(ext =>
                                     ext.Uri == "http://www.ietf.org/id/draft-holmer-rmcat-transport-wide-cc-extensions-01"))
            {
                foreach (var codec in codecs)
                {
                    codec.RtcpFeedback = codec.RtcpFeedback
                                         .Where(fb => fb.Type != "goog-remb").ToArray();
                }
            }
            else if (headerExtensions.Any(ext =>
                                          ext.Uri == "http://www.webrtc.org/experiments/rtp-hdrext/abs-send-time"))
            {
                foreach (var codec in codecs)
                {
                    codec.RtcpFeedback = codec.RtcpFeedback
                                         .Where(fb => fb.Type != "transport-cc").ToArray();
                }
            }
            else
            {
                foreach (var codec in codecs)
                {
                    codec.RtcpFeedback = codec.RtcpFeedback
                                         .Where(fb => fb.Type != "transport-cc" && fb.Type != "goog-remb").ToArray();
                }
            }

            return(new RtpParameters
            {
                Codecs = codecs.ToArray(),
                HeaderExtensions = headerExtensions.ToArray(),
                Encodings = new RtpEncodingParameters[] { },
                Rtcp = new()
            });
        }
Exemple #20
0
 public bool CanSend(MediaKind kind, ExtendedRtpCapabilities extendedRtpCapabilities)
 {
     return(extendedRtpCapabilities.Codecs.Any(codec => codec.Kind == kind));
 }
Exemple #21
0
 /// <summary>
 /// Create a new media source of the given <see cref="MediaKind"/>.
 /// </summary>
 /// <param name="mediaKind">The media kind of the source.</param>
 public MediaSource(MediaKind mediaKind)
 {
     MediaKind = mediaKind;
 }
Exemple #22
0
        public Task <IList <IMediaDevice> > GetMediaDevicesAsync(MediaKind kind)
        {
            if (kind == MediaKind.AudioInputDevice)
            {
                return(Task.Run(async() =>
                {
                    IList <IMediaDevice> audioMediaDevicesCapturersList = new List <IMediaDevice>();

                    DeviceInformationCollection audioCapturers = await DeviceInformation.FindAllAsync(Windows.Media.Devices.MediaDevice.GetAudioCaptureSelector());

                    foreach (var microphone in audioCapturers)
                    {
                        var mediaDevice = new MediaDevice();
                        mediaDevice.GetMediaKind(MediaKind.AudioInputDevice.ToString());
                        mediaDevice.GetId(microphone.Id);
                        mediaDevice.GetDisplayName(microphone.Name);

                        audioMediaDevicesCapturersList.Add(mediaDevice);
                    }
                    return audioMediaDevicesCapturersList;
                }));
            }

            if (kind == MediaKind.AudioOutputDevice)
            {
                return(Task.Run(async() =>
                {
                    IList <IMediaDevice> audioMediaDevicesRendersList = new List <IMediaDevice>();

                    DeviceInformationCollection audioRenders = await DeviceInformation.FindAllAsync(Windows.Media.Devices.MediaDevice.GetAudioRenderSelector());

                    foreach (var speaker in audioRenders)
                    {
                        var mediaDevice = new MediaDevice();
                        mediaDevice.GetMediaKind(MediaKind.AudioOutputDevice.ToString());
                        mediaDevice.GetId(speaker.Id);
                        mediaDevice.GetDisplayName(speaker.Name);

                        audioMediaDevicesRendersList.Add(mediaDevice);
                    }
                    return audioMediaDevicesRendersList;
                }));
            }

            if (kind == MediaKind.VideoDevice)
            {
                return(Task.Run(async() =>
                {
                    IList <IMediaDevice> videoMediaDevicesList = new List <IMediaDevice>();

                    IReadOnlyList <IVideoDeviceInfo> videoDevices = await VideoCapturer.GetDevices();

                    foreach (IVideoDeviceInfo videoDevice in videoDevices)
                    {
                        var mediaDevice = new MediaDevice();
                        mediaDevice.GetMediaKind(MediaKind.VideoDevice.ToString());
                        mediaDevice.GetId(videoDevice.Info.Id);
                        mediaDevice.GetDisplayName(videoDevice.Info.Name);

                        IList <MediaVideoFormat> videoFormatsList = await GetMediaVideoFormatList(videoDevice.Info.Id);

                        mediaDevice.GetVideoFormats(videoFormatsList);

                        videoMediaDevicesList.Add(mediaDevice);
                    }
                    return videoMediaDevicesList;
                }));
            }
            else
            {
                return(null);
            }
        }
 /// <summary>
 /// Constructor called internally by <see cref="PeerConnection.AddMediaLine(MediaKind)"/>.
 /// </summary>
 /// <param name="kind">Immutable value assigned to the <see cref="MediaKind"/> property on construction.</param>
 internal MediaLine(PeerConnection peer, MediaKind kind)
 {
     _peer      = peer;
     _mediaKind = kind;
 }
Exemple #24
0
        /// <summary>
        /// Parses the Show specific data from the input string and the match.
        /// </summary>
        /// <param name="input">Input string</param>
        /// <param name="match">The returned matches for the regexes.</param>
        /// <param name="dir">Switch whenever it is a directory name.</param>
        /// <param name="AirDateInName">Switch whenever the date regex matched.</param>
        private void ParseShow(String input, Match match, Boolean dir = false, Boolean AirDateInName = false)
        {
            mediaKind = EMP.MediaKind.Show;
            //String inputCl = helperDictionary.CleanFileName(input);//unnneeded
            GroupCollection groups = match.Groups;
            #region Season, Episode or AirDate
            if (match.Length > 0)
                Index.Add(new StringLocation(match.ToString(), match.Index, match.Length, false, dir));
            if (AirDateInName)
            {
                if (airDate == null)
                {
                    DateTime.TryParse(match.ToString(), out airDate);
                }
            }
            else
            {
                if (season == 0)
                {
                    if (!Int16.TryParse(groups[1].ToString(), out season) || groups[1].ToString().Trim() == String.Empty)
                    {
                        Int16.TryParse(groups[3].ToString(), out season);
                    }
                }
                if (episode == 0)
                {
                    if (!Int16.TryParse(groups[2].ToString(), out episode) || groups[2].ToString().Trim() == String.Empty)
                    {
                        Int16.TryParse(groups[4].ToString(), out episode);
                    }
                }
            }
            #endregion
            //title is series and the shit in between episode number and the rest is series title.
            #region Series, Title & Other

            #endregion
        }
Exemple #25
0
        /// <summary>
        /// Parses the Movie specific data from the input string.
        /// </summary>
        /// <param name="input">Input string</param>
        /// <param name="dir">Switch whenever it is a directory name.</param>
        private void ParseMovie(String input, Boolean dir = false)
        {
            mediaKind = EMP.MediaKind.Movie;
            String inputCl = helperDictionary.CleanFileName(input);
            Int32 TmpStart;
            String TmpString;
            #region year
            //A little regex for recognizing the year
            Regex rgx = new Regex(@"((19|20)\d{2})");

            if (rgx.IsMatch(input))
            {
                if (year == 0)
                {
                    Match match = rgx.Matches(inputCl)[rgx.Matches(input).Count - 1];
                    TmpString = match.ToString();
                    if (match.Length > 0)
                        Index.Add(new StringLocation(TmpString, match.Index, match.Length, true, dir));
                    Int32.TryParse(TmpString, out year);
                }
            }
            #endregion
            #region cut
            if (cut == Cut.Final)
            {
                TmpString = Check(inputCl, helperDictionary.CutStrings, out TmpStart);
                cut = helperDictionary.StrToCut(TmpString);
                if (TmpString.Length > 0)
                    Index.Add(new StringLocation(TmpString, TmpStart, TmpString.Length, true, dir));
            }
            #endregion
            #region IMDBid
            if (imdbid == String.Empty || imdbid == null)
            {
                // Here we call Regex.Match.
                Match match = Regex.Match(input, @"tt[0-9]{7}",
                    RegexOptions.IgnoreCase);

                // Here we check the Match instance.
                if (match.Success)
                {
                    // Finally, we get the Group value and display it.
                    imdbid = match.Value;
                    Index.Add(new StringLocation(match.Value, match.Index, match.Length, false, dir));
                }
            }
            #endregion
        }
Exemple #26
0
 /// <inheritdoc/>
 public MediaSender(MediaKind mediaKind) : base(mediaKind)
 {
 }
 /// <summary>
 /// Constructor called internally by <see cref="PeerConnection.AddMediaLine(MediaKind)"/>.
 /// </summary>
 /// <param name="kind">Immutable value assigned to the <see cref="MediaKind"/> property on construction.</param>
 internal MediaLine(MediaKind kind)
 {
     _mediaKind = kind;
 }
Exemple #28
0
 public Transceiver AddTransceiver(MediaKind mediaKind, TransceiverInitSettings settings)
 {
     // This will raise the TransceiverAdded event, which adds a new view model
     // for the newly added transceiver automatically.
     return(_peerConnection.AddTransceiver(mediaKind, settings));
 }
 public Task AddTransceivers_TransceiverAddedExplicitly_ReturnsTransceiverWithCorrectMediaKind(MediaKind mediaKind)
 {
     return(_signallingThread.ExecuteAsync(delegate
     {
         using (var peerConnectionObserver = new PeerConnectionObserver())
             using (var peerConnection = _peerConnectionFactory.CreatePeerConnection(peerConnectionObserver, _config))
                 using (var transceiver = peerConnection.AddTransceiver(mediaKind, RtpTransceiverDirection.SendOnly))
                 {
                     Assert.NotNull(transceiver);
                     Assert.Equal(mediaKind, transceiver.MediaKind);
                 }
     }));
 }
 /// <inheritdoc/>
 public MediaReceiver(MediaKind mediaKind) : base(mediaKind)
 {
 }
Exemple #31
0
 public TransceiverTests(SdpSemantic sdpSemantic, MediaKind mediaKind) : base(sdpSemantic)
 {
     MediaKind = mediaKind;
 }
        void OnEnable()
        {
            autoCreateOffer_ = serializedObject.FindProperty("AutoCreateOfferOnRenegotiationNeeded");
            autoLogErrors_   = serializedObject.FindProperty("AutoLogErrorsToUnityConsole");

            iceServers_    = serializedObject.FindProperty("IceServers");
            iceUsername_   = serializedObject.FindProperty("IceUsername");
            iceCredential_ = serializedObject.FindProperty("IceCredential");

            onInitialized_ = serializedObject.FindProperty("OnInitialized");
            onShutdown_    = serializedObject.FindProperty("OnShutdown");
            onError_       = serializedObject.FindProperty("OnError");

            mediaLines_      = serializedObject.FindProperty("_mediaLines");
            transceiverList_ = new ReorderableList(serializedObject, mediaLines_, draggable: true,
                                                   displayHeader: true, displayAddButton: false, displayRemoveButton: true);
            transceiverList_.elementHeightCallback =
                (int index) =>
            {
                float height  = kItemSpacing + 2 * kLineHeight;
                var   element = transceiverList_.serializedProperty.GetArrayElementAtIndex(index);
                var   src     = element.FindPropertyRelative("_source");
                if (src.isExpanded)
                {
                    var trackName = element.FindPropertyRelative("SenderTrackName");
                    // FIXME - SdpTokenDrawer.OnGUI() is called with h=16px instead of the total height, breaking the layout
                    height += kLineHeight;     // EditorGUI.GetPropertyHeight(trackName) + kItemSpacing;
                }
                return(height);
            };
            transceiverList_.drawHeaderCallback  = (Rect rect) => EditorGUI.LabelField(rect, "Transceivers");
            transceiverList_.drawElementCallback =
                (Rect rect, int index, bool isActive, bool isFocused) =>
            {
                var   element = transceiverList_.serializedProperty.GetArrayElementAtIndex(index);
                float x0      = rect.x;
                float x1      = x0 + 16;
                float y0      = rect.y + 2;
                float y1      = y0 + kLineHeight;

                // MID value
                EditorGUI.LabelField(new Rect(x0 - 14, y1, 20, 20), $"{index}");

                // Audio or video icon for transceiver kind
                MediaKind   mediaKind = (MediaKind)element.FindPropertyRelative("_mediaKind").intValue;
                System.Type senderType, receiverType;
                if (mediaKind == MediaKind.Audio)
                {
                    senderType   = typeof(AudioTrackSource);
                    receiverType = typeof(AudioReceiver);
                    DrawSpriteIcon(IconType.Audio, new Rect(x0, rect.y, 20, 20));
                }
                else
                {
                    senderType   = typeof(VideoTrackSource);
                    receiverType = typeof(VideoReceiver);
                    DrawSpriteIcon(IconType.Video, new Rect(x0, rect.y, 20, 20));
                }

                rect.x     += (kIconSpacing + 10);
                rect.width -= (kIconSpacing + 10);

                float fieldWidth       = rect.width;
                bool  hasSender        = false;
                bool  hasReceiver      = false;
                bool  sourceIsExpanded = false;
                {
                    var    p   = element.FindPropertyRelative("_source");
                    Object obj = p.objectReferenceValue;
                    sourceIsExpanded = EditorGUI.Foldout(new Rect(rect.x, y0, 0, EditorGUIUtility.singleLineHeight), p.isExpanded, new GUIContent());
                    p.isExpanded     = sourceIsExpanded;
                    obj = EditorGUI.ObjectField(
                        new Rect(rect.x, y0, fieldWidth, EditorGUIUtility.singleLineHeight),
                        obj, senderType, true);
                    hasSender = (obj != null);
                    p.objectReferenceValue = obj;
                    y0 += kLineHeight;
                }
                if (sourceIsExpanded)
                {
                    var p = element.FindPropertyRelative("_senderTrackName");
                    // FIXME - SdpTokenDrawer.OnGUI() is called with h=16px instead of the total height, breaking the layout
                    //EditorGUI.PropertyField(new Rect(rect.x + 10, y0, fieldWidth - 8, EditorGUIUtility.singleLineHeight), p);
                    //y0 += EditorGUI.GetPropertyHeight(p) + 6;
                    string val = p.stringValue;
                    val           = EditorGUI.TextField(new Rect(rect.x + 10, y0, fieldWidth - 8, EditorGUIUtility.singleLineHeight), "Track name", val);
                    p.stringValue = val;
                    y0           += kLineHeight;
                }
                {
                    var    p   = element.FindPropertyRelative("_receiver");
                    Object obj = p.objectReferenceValue;
                    obj = EditorGUI.ObjectField(
                        new Rect(rect.x, y0, fieldWidth, EditorGUIUtility.singleLineHeight),
                        obj, receiverType, true);
                    hasReceiver            = (obj != null);
                    p.objectReferenceValue = obj;
                }

                IconType iconType = IconType.Inactive;
                if (hasSender)
                {
                    if (hasReceiver)
                    {
                        iconType = IconType.SendRecv;
                    }
                    else
                    {
                        iconType = IconType.SendOnly;
                    }
                }
                else if (hasReceiver)
                {
                    iconType = IconType.RecvOnly;
                }
                DrawSpriteIcon(iconType, new Rect(x0, y1, 16, 16));
            };
            transceiverList_.drawNoneElementCallback = (Rect rect) =>
            {
                GUIStyle style = new GUIStyle(EditorStyles.label);
                style.alignment = TextAnchor.MiddleCenter;
                EditorGUI.LabelField(rect, "(empty)", style);
            };
        }
        //readonly SctpParameters _sctpParameters;
        //readonly PlainRtpParameters _plainRtpParameters;
        //readonly Mid _mid;
        //readonly MediaKind _kind;
        //RtpParameters

        public OfferMediaSection(
            IceParameters iceParameters,
            IceCandidate[] iceCandidates,
            DtlsParameters dtlsParameters,
            SctpParameters sctpParameters,
            PlainRtpParameters plainRtpParameters,
            bool planB,
            Mid mid,
            MediaKind kind,
            RtpParameters offerRtpParameters,
            string streamId,
            string trackId,
            bool oldDataChannelSpec) : base(iceParameters, iceCandidates, dtlsParameters, planB)
        {
            _mediaObject.MediaDescription.Attributes.Mid = mid;
            _mediaObject.MediaDescription.Media          = kind.ToSdp();

            if (plainRtpParameters is null)
            {
                _mediaObject.MediaDescription.ConnectionData = new ConnectionData
                {
                    NetType           = NetType.Internet,
                    AddrType          = AddrType.Ip4,
                    ConnectionAddress = "127.0.0.1"
                };
                if (sctpParameters is null)
                {
                    _mediaObject.MediaDescription.Proto = "UDP/TLS/RTP/SAVPF";
                }
                else
                {
                    _mediaObject.MediaDescription.Proto = "UDP/DTLS/SCTP";
                }
                _mediaObject.MediaDescription.Port = 7;
            }
            else
            {
                _mediaObject.MediaDescription.ConnectionData = new ConnectionData
                {
                    NetType           = NetType.Internet,
                    AddrType          = plainRtpParameters.IpVersion,
                    ConnectionAddress = plainRtpParameters.Ip
                };
                _mediaObject.MediaDescription.Proto = "RTP/AVP";
                _mediaObject.MediaDescription.Port  = plainRtpParameters.Port;
            }

            switch (kind)
            {
            case MediaKind.Audio:
            case MediaKind.Video:
            {
                _mediaObject.MediaDescription.Attributes.SendOnly = true;
                _mediaObject.MediaDescription.Attributes.Rtpmaps  = new List <Rtpmap>();
                _mediaObject.MediaDescription.Attributes.RtcpFbs  = new List <RtcpFb>();
                _mediaObject.MediaDescription.Attributes.Fmtps    = new List <Fmtp>();

                if (!_planB)
                {
                    _mediaObject.MediaDescription.Attributes.Msid = new()
                    {
                        Id      = streamId ?? "-",
                        AppData = trackId
                    }
                }
                ;

                foreach (var codec in offerRtpParameters.Codecs)
                {
                    Rtpmap rtpmap = new()
                    {
                        PayloadType  = codec.PayloadType,
                        EncodingName = GetCodecName(codec),
                        ClockRate    = codec.ClockRate,
                        Channels     = codec.Channels > 1 ? codec.Channels : null
                    };
                    _mediaObject.MediaDescription.Attributes.Rtpmaps.Add(rtpmap);

                    Fmtp fmtp = codec.Parameters.ToFmtp(codec.PayloadType);
                    if (!string.IsNullOrEmpty(fmtp.Value))
                    {
                        _mediaObject.MediaDescription.Attributes.Fmtps.Add(fmtp);
                    }

                    foreach (var fb in codec.RtcpFeedback)
                    {
                        RtcpFb rtcpFb = new()
                        {
                            PayloadType = codec.PayloadType,
                            Type        = fb.Type,
                            SubType     = fb.Parameter
                        };
                        _mediaObject.MediaDescription.Attributes.RtcpFbs.Add(rtcpFb);
                    }
                }

                _mediaObject.MediaDescription.Fmts ??= new List <string>();
                ((List <string>)_mediaObject.MediaDescription.Fmts).AddRange(offerRtpParameters.Codecs
                                                                             .Select(codec => codec.PayloadType.ToString()));


                _mediaObject.MediaDescription.Attributes.Extmaps = new List <Extmap>();
                foreach (var headerExtension in offerRtpParameters.HeaderExtensions)
                {
                    Extmap ext = new()
                    {
                        Uri   = new Uri(headerExtension.Uri),
                        Value = headerExtension.Id
                    };
                    _mediaObject.MediaDescription.Attributes.Extmaps.Add(ext);
                }

                _mediaObject.MediaDescription.Attributes.RtcpMux   = true;
                _mediaObject.MediaDescription.Attributes.RtcpRsize = true;

                var encoding = offerRtpParameters.Encodings[0];
                var ssrc     = encoding.Ssrc;
                var rtxSsrc  = (encoding.Rtx is not null && encoding.Rtx.Ssrc.HasValue) ?
                               encoding.Rtx.Ssrc : null;

                _mediaObject.MediaDescription.Attributes.Ssrcs      = new List <Ssrc>();
                _mediaObject.MediaDescription.Attributes.SsrcGroups = new List <SsrcGroup>();
                if (offerRtpParameters.Rtcp.Cname is not null)
                {
                    _mediaObject.MediaDescription.Attributes.Ssrcs.Add(new Ssrc
                        {
                            Id        = (uint)ssrc,
                            Attribute = "cname",
                            Value     = offerRtpParameters.Rtcp.Cname
                        });
                }

                if (_planB)
                {
                    _mediaObject.MediaDescription.Attributes.Ssrcs.Add(new Ssrc
                        {
                            Id        = (uint)ssrc,
                            Attribute = "msid",
                            Value     = $"{streamId ?? "-"} {trackId}"
                        });
                }

                if (rtxSsrc.HasValue)
                {
                    if (offerRtpParameters.Rtcp.Cname is not null)
                    {
                        _mediaObject.MediaDescription.Attributes.Ssrcs.Add(new Ssrc
                            {
                                Id        = (uint)rtxSsrc,
                                Attribute = "cname",
                                Value     = offerRtpParameters.Rtcp.Cname
                            });
                    }

                    if (_planB)
                    {
                        _mediaObject.MediaDescription.Attributes.Ssrcs.Add(new Ssrc
                            {
                                Id        = (uint)rtxSsrc,
                                Attribute = "msid",
                                Value     = $"{streamId ?? "-"} {trackId}"
                            });
                    }

                    // Associate original and retransmission SSRCs.
                    _mediaObject.MediaDescription.Attributes.SsrcGroups.Add(new SsrcGroup
                        {
                            Semantics = "FID",
                            SsrcIds   = new string[] { $"{ssrc} {rtxSsrc}" }
                        });
                }

                break;
            }

            case MediaKind.Application:
            {
                // New spec.
                if (!oldDataChannelSpec)
                {
                    _mediaObject.MediaDescription.Fmts = new List <string> {
                        "webrtc-datachannel"
                    };
                    _mediaObject.MediaDescription.Attributes.SctpPort = new SctpPort
                    {
                        Port = sctpParameters.Port
                    };
                    _mediaObject.MediaDescription.Attributes.MaxMessageSize = new MaxMessageSize
                    {
                        Size = sctpParameters.MaxMessageSize
                    };
                }
                // Old spec.
                else
                {
                    _mediaObject.MediaDescription.Fmts = new List <string> {
                        sctpParameters.Port.ToString()
                    };
                    _mediaObject.SctpMap = new()
                    {
                        App            = "webrtc-datachannel",
                        SctpMapNumber  = sctpParameters.Port,
                        MaxMessageSize = sctpParameters.MaxMessageSize
                    };
                }

                break;
            }
            }
        }

        void PlanBReceive(RtpParameters offerRtpParameters, string streamId, IMediaStream trackId)
        {
            var encoding = offerRtpParameters.Encodings[0];
            var ssrc     = encoding.Ssrc;
            var rtxSsrc  = (encoding.Rtx is not null && encoding.Rtx.Ssrc.HasValue) ?
                           encoding.Rtx.Ssrc : null;
            var payloads = _mediaObject.MediaDescription.Fmts.ToArray();


            foreach (var codec in offerRtpParameters.Codecs)
            {
                if (payloads.Any(payload => payload.Contains(codec.PayloadType.ToString())))
                {
                    continue;
                }

                Rtpmap rtpmap = new()
                {
                    PayloadType  = codec.PayloadType,
                    EncodingName = GetCodecName(codec),
                    ClockRate    = codec.ClockRate,
                    Channels     = codec.Channels > 1 ? codec.Channels : null
                };
                _mediaObject.MediaDescription.Attributes.Rtpmaps.Add(rtpmap);

                Fmtp fmtp = codec.Parameters.ToFmtp(codec.PayloadType);
                if (!string.IsNullOrEmpty(fmtp.Value))
                {
                    _mediaObject.MediaDescription.Attributes.Fmtps.Add(fmtp);
                }

                foreach (var fb in codec.RtcpFeedback)
                {
                    RtcpFb rtcpFb = new()
                    {
                        PayloadType = codec.PayloadType,
                        Type        = fb.Type,
                        SubType     = fb.Parameter
                    };
                    _mediaObject.MediaDescription.Attributes.RtcpFbs.Add(rtcpFb);
                }
            }

            ((List <string>)_mediaObject.MediaDescription.Fmts).AddRange(offerRtpParameters.Codecs
                                                                         .Select(codec => codec.PayloadType.ToString()));


            if (offerRtpParameters.Rtcp.Cname is not null)
            {
                _mediaObject.MediaDescription.Attributes.Ssrcs.Add(new Ssrc
                {
                    Id        = (uint)ssrc,
                    Attribute = "cname",
                    Value     = offerRtpParameters.Rtcp.Cname
                });
            }

            _mediaObject.MediaDescription.Attributes.Ssrcs.Add(new Ssrc
            {
                Id        = (uint)ssrc,
                Attribute = "msid",
                Value     = $"{streamId ?? "-"} {trackId}"
            });

            if (rtxSsrc.HasValue)
            {
                if (offerRtpParameters.Rtcp.Cname is not null)
                {
                    _mediaObject.MediaDescription.Attributes.Ssrcs.Add(new Ssrc
                    {
                        Id        = (uint)rtxSsrc,
                        Attribute = "cname",
                        Value     = offerRtpParameters.Rtcp.Cname
                    });
                }

                _mediaObject.MediaDescription.Attributes.Ssrcs.Add(new Ssrc
                {
                    Id        = (uint)rtxSsrc,
                    Attribute = "msid",
                    Value     = $"{streamId ?? "-"} {trackId}"
                });

                // Associate original and retransmission SSRCs.
                _mediaObject.MediaDescription.Attributes.SsrcGroups.Add(new SsrcGroup
                {
                    Semantics = "FID",
                    SsrcIds   = new string[] { $"{ssrc} {rtxSsrc}" }
                });
            }
        }

        public void PlanBReceive(RtpParameters offerRtpParameters, string streamId, string trackId)
        {
            var encoding = offerRtpParameters.Encodings[0];
            var ssrc     = encoding.Ssrc;
            var rtxSsrc  = (encoding.Rtx is not null && encoding.Rtx.Ssrc.HasValue)
                ? encoding.Rtx.Ssrc
                : null;
            var payloads = _mediaObject.MediaDescription.Fmts.ToArray();

            foreach (var codec in offerRtpParameters.Codecs)
            {
                if (payloads.Contains(codec.PayloadType.ToString()))
                {
                    continue;
                }

                Rtpmap rtpmap = new()
                {
                    PayloadType  = codec.PayloadType,
                    EncodingName = GetCodecName(codec),
                    ClockRate    = codec.ClockRate,
                    Channels     = codec.Channels > 1 ? codec.Channels : null
                };
                _mediaObject.MediaDescription.Attributes.Rtpmaps.Add(rtpmap);

                Fmtp fmtp = codec.Parameters.ToFmtp(codec.PayloadType);

                if (!string.IsNullOrEmpty(fmtp.Value))
                {
                    _mediaObject.MediaDescription.Attributes.Fmtps.Add(fmtp);
                }

                foreach (var fb in codec.RtcpFeedback)
                {
                    _mediaObject.MediaDescription.Attributes.RtcpFbs.Add(new RtcpFb
                    {
                        PayloadType = codec.PayloadType,
                        Type        = fb.Type,
                        SubType     = fb.Parameter
                    });
                }
            }

            ((List <string>)_mediaObject.MediaDescription.Fmts).AddRange(offerRtpParameters.Codecs
                                                                         .Where(codec => !_mediaObject.MediaDescription.Fmts.Contains(codec.PayloadType.ToString()))
                                                                         .Select(codec => codec.PayloadType.ToString())
                                                                         .ToArray());

            if (offerRtpParameters.Rtcp.Cname is not null)
            {
                _mediaObject.MediaDescription.Attributes.Ssrcs.Add(new Ssrc
                {
                    Id        = (uint)ssrc,
                    Attribute = "cname",
                    Value     = offerRtpParameters.Rtcp.Cname
                });
            }

            _mediaObject.MediaDescription.Attributes.Ssrcs.Add(new Ssrc
            {
                Id        = (uint)ssrc,
                Attribute = "msid",
                Value     = $"{ streamId ?? "-"} { trackId}"
            });

            if (rtxSsrc is not null)
            {
                if (offerRtpParameters.Rtcp.Cname is not null)
                {
                    _mediaObject.MediaDescription.Attributes.Ssrcs.Add(new Ssrc
                    {
                        Id        = (uint)rtxSsrc,
                        Attribute = "cname",
                        Value     = offerRtpParameters.Rtcp.Cname
                    });
                }

                _mediaObject.MediaDescription.Attributes.Ssrcs.Add(new Ssrc
                {
                    Id        = (uint)rtxSsrc,
                    Attribute = "msid",
                    Value     = $"{ streamId ?? "-"} {trackId}"
                });

                // Associate original and retransmission SSRCs.
                _mediaObject.MediaDescription.Attributes.SsrcGroups.Add(new SsrcGroup
                {
                    Semantics = "FID",
                    SsrcIds   = new string[] { $"{ ssrc }${rtxSsrc}" }
                });
            }
        }

        void PlanBStopReceiving(RtpParameters offerRtpParameters)
        {
            var encoding = offerRtpParameters.Encodings[0];
            var ssrc     = encoding.Ssrc;
            var rtxSsrc  = (encoding.Rtx is not null && encoding.Rtx.Ssrc.HasValue) ?
                           encoding.Rtx.Ssrc : null;

            ((List <Ssrc>)_mediaObject.MediaDescription.Attributes.Ssrcs).RemoveAll(s => s.Id != ssrc && s.Id != rtxSsrc);

            if (rtxSsrc is not null)
            {
                ((List <SsrcGroup>)_mediaObject.MediaDescription.Attributes.SsrcGroups).RemoveAll(g => g.SsrcIds.Any(id => id != $"{ssrc} {rtxSsrc}"));
            }
        }

        public override void SetDtlsRole(DtlsRole?dtlsRole)