public void Parse_TestDocumentWithVideoAndAudioTracks_ReturnsTwoTracks()
        {
            string testInput = "m=video 0 RTP/AVP 96\r\n" +
                               "a=control:streamid=0\r\n" +
                               "a=range:npt=0-7.741000\r\n" +
                               "a=length:npt=7.741000\r\n" +
                               "a=rtpmap:96 H264/90000\r\n" +
                               "a=StreamName:string;\"hinted video track\"\r\n" +
                               "a=fmtp:96 packetization-mode=1; profile-level-id=420029; sprop-parameter-sets=Z0IAKeKQCgDLYC3AQEBpB4kRUA==,aM48gA==\r\n" +
                               "m=audio 0 RTP/AVP 97\r\n" +
                               "a=control:streamid=1\r\n" +
                               "a=range:npt=0-7.712000\r\n" +
                               "a=length:npt=7.712000\r\n" +
                               "a=rtpmap:97 mpeg4-generic/32000/2\r\n" +
                               "a=mimetype:string;\"audio/mpeg4-generic\"\r\n" +
                               "a=AvgBitRate:integer;65790\r\n" +
                               "a=StreamName:string;\"hinted audio track\"\r\n";

            var testBytes = Encoding.ASCII.GetBytes(testInput);

            var parser = new SdpParser();
            IReadOnlyList <RtspMediaTrackInfo> tracks = parser.Parse(testBytes).Where(t => t is RtspMediaTrackInfo)
                                                        .Cast <RtspMediaTrackInfo>().ToList();

            Assert.AreEqual(2, tracks.Count);
            RtspMediaTrackInfo videoTrack = tracks.First(t => t.Codec is VideoCodecInfo);

            Assert.AreEqual("streamid=0", videoTrack.TrackName);
            Assert.AreEqual(90000, videoTrack.SamplesFrequency);
            RtspMediaTrackInfo audioTrack = tracks.First(t => t.Codec is AudioCodecInfo);

            Assert.AreEqual("streamid=1", audioTrack.TrackName);
            Assert.AreEqual(32000, audioTrack.SamplesFrequency);
        }
        public void Parse_SDPWithJPEGTrack_ReturnsWithJPEGCodecTrack()
        {
            string testInput = "m=video 0 RTP/AVP 26\r\n" +
                               "c=IN IP4 0.0.0.0\r\n" +
                               "a=control:track1\r\n";

            var testBytes = Encoding.ASCII.GetBytes(testInput);

            var parser = new SdpParser();
            RtspMediaTrackInfo videoTrack = parser.Parse(testBytes).Where(t => t is RtspMediaTrackInfo)
                                            .Cast <RtspMediaTrackInfo>().First();

            Assert.IsInstanceOfType(videoTrack.Codec, typeof(MJPEGCodecInfo));
        }
        public void Parse_SDPWithG711UTrack_ReturnsWithG711UCodecTrack()
        {
            string testInput = "m=audio 0 RTP/AVP 0\r\n" +
                               "a=rtpmap:0 PCMU/16000/2\r\n" +
                               "a=control:trackID=2\r\n";

            var testBytes = Encoding.ASCII.GetBytes(testInput);

            var parser = new SdpParser();
            RtspMediaTrackInfo audioTrack = parser.Parse(testBytes).Where(t => t is RtspMediaTrackInfo)
                                            .Cast <RtspMediaTrackInfo>().First();

            G711UCodecInfo codecInfo = (G711UCodecInfo)audioTrack.Codec;

            Assert.AreEqual(2, codecInfo.Channels);
            Assert.AreEqual(16000, codecInfo.SampleRate);
        }
        public void Parse_SDPWithG726Track_ReturnsWithG726CodecTrack(int bitrate)
        {
            string testInput = "m=audio 0 RTP/AVP 97\r\n" +
                               "a=control:trackID=1\r\n" +
                               $"a=rtpmap:97 G726-{bitrate}/8000\r\n";

            var testBytes = Encoding.ASCII.GetBytes(testInput);

            var parser = new SdpParser();
            RtspMediaTrackInfo audioTrack = parser.Parse(testBytes).Where(t => t is RtspMediaTrackInfo)
                                            .Cast <RtspMediaTrackInfo>().First();

            G726CodecInfo codecInfo = (G726CodecInfo)audioTrack.Codec;

            Assert.AreEqual(1, codecInfo.Channels);
            Assert.AreEqual(8000, codecInfo.SampleRate);
            Assert.AreEqual(bitrate * 1000, codecInfo.Bitrate);
        }
        public void Parse_SDPWithPCMTrack_ReturnsWithPCMCodecTrack(int bitsPerSample)
        {
            string testInput = "m=audio 0 RTP/AVP 97\r\n" +
                               "c=IN IP4 0.0.0.0\r\n" +
                               "b=AS:0\r\n" +
                               $"a=rtpmap:97 L{bitsPerSample}/16000\r\n" +
                               "a=control:track2\r\n";

            var testBytes = Encoding.ASCII.GetBytes(testInput);

            var parser = new SdpParser();
            RtspMediaTrackInfo audioTrack = parser.Parse(testBytes).Where(t => t is RtspMediaTrackInfo)
                                            .Cast <RtspMediaTrackInfo>().First();

            PCMCodecInfo codecInfo = (PCMCodecInfo)audioTrack.Codec;

            Assert.AreEqual(1, codecInfo.Channels);
            Assert.AreEqual(16000, codecInfo.SampleRate);
            Assert.AreEqual(bitsPerSample, codecInfo.BitsPerSample);
        }
        public void Parse_SDPWithH264Track_ReturnsWithH264CodecTrack()
        {
            IEnumerable <byte> spsBytes =
                RawH264Frame.StartMarker.Concat(Convert.FromBase64String("Z2QAFKzZQ0R+f/zBfMMAQAAAAwBAAAAKI8UKZYA="));
            IEnumerable <byte> ppsBytes = RawH264Frame.StartMarker.Concat(Convert.FromBase64String("aOvssiw="));

            string testInput = "m=video 0 RTP/AVP 96\r\n" +
                               "a=control:streamid=0\r\n" +
                               "a=rtpmap:96 H264/90000\r\n" +
                               "a=fmtp:96 profile-level-id=640014;sprop-parameter-sets=Z2QAFKzZQ0R+f/zBfMMAQAAAAwBAAAAKI8UKZYA=,aOvssiw=\r\n";

            var testBytes = Encoding.ASCII.GetBytes(testInput);

            var parser = new SdpParser();
            RtspMediaTrackInfo videoTrack = parser.Parse(testBytes).Where(t => t is RtspMediaTrackInfo)
                                            .Cast <RtspMediaTrackInfo>().First();

            H264CodecInfo codecInfo = (H264CodecInfo)videoTrack.Codec;

            Assert.IsTrue(spsBytes.Concat(ppsBytes).SequenceEqual(codecInfo.SpsPpsBytes));
        }
        public void Parse_SDPWithAACTrack_ReturnsWithAACCodecTrack()
        {
            var    configBytes = new byte[] { 0x12, 0x10 };
            string testInput   = "m=audio 0 RTP/AVP 96\r\n" +
                                 "b=AS:128\r\n" +
                                 "a=rtpmap:96 MPEG4-GENERIC/44100/2\r\n" +
                                 "a=fmtp:96 profile-level-id=1;mode=AAC-hbr;sizelength=13;indexlength=3;indexdeltalength=3; config=1210\r\n" +
                                 "a=control:streamid=0\r\n";

            var testBytes = Encoding.ASCII.GetBytes(testInput);

            var parser = new SdpParser();
            RtspMediaTrackInfo audioTrack = parser.Parse(testBytes).Where(t => t is RtspMediaTrackInfo)
                                            .Cast <RtspMediaTrackInfo>().First();

            AACCodecInfo codecInfo = (AACCodecInfo)audioTrack.Codec;

            Assert.AreEqual(3, codecInfo.IndexDeltaLength);
            Assert.AreEqual(3, codecInfo.IndexLength);
            Assert.AreEqual(13, codecInfo.SizeLength);
            Assert.IsTrue(configBytes.SequenceEqual(codecInfo.ConfigBytes));
        }
        private async Task SetupTrackAsync(RtspMediaTrackInfo track, CancellationToken token)
        {
            RtspRequestMessage  setupRequest;
            RtspResponseMessage setupResponse;

            int    rtpChannelNumber;
            int    rtcpChannelNumber;
            Socket rtpClient  = null;
            Socket rtcpClient = null;

            if (_connectionParameters.RtpTransport == RtpTransportProtocol.UDP)
            {
                rtpClient  = NetworkClientFactory.CreateUdpClient();
                rtcpClient = NetworkClientFactory.CreateUdpClient();

                try
                {
                    IPEndPoint endPoint = new IPEndPoint(IPAddress.Any, 0);
                    rtpClient.Bind(endPoint);

                    int rtpPort = ((IPEndPoint)rtpClient.LocalEndPoint).Port;

                    endPoint = new IPEndPoint(IPAddress.Any, rtpPort + 1);

                    try
                    {
                        rtcpClient.Bind(endPoint);
                    }
                    catch (SocketException e) when(e.SocketErrorCode == SocketError.AddressAlreadyInUse)
                    {
                        endPoint = new IPEndPoint(IPAddress.Any, 0);
                        rtcpClient.Bind(endPoint);
                    }

                    int rtcpPort = ((IPEndPoint)rtcpClient.LocalEndPoint).Port;

                    setupRequest = _requestMessageFactory.CreateSetupUdpUnicastRequest(track.TrackName,
                                                                                       rtpPort, rtcpPort);
                    setupResponse = await _rtspTransportClient.EnsureExecuteRequest(setupRequest, token);
                }
                catch
                {
                    rtpClient.Close();
                    rtcpClient.Close();
                    throw;
                }
            }
            else
            {
                int channelCounter = _streamsMap.Count;
                rtpChannelNumber  = channelCounter;
                rtcpChannelNumber = ++channelCounter;

                setupRequest = _requestMessageFactory.CreateSetupTcpInterleavedRequest(track.TrackName,
                                                                                       rtpChannelNumber, rtcpChannelNumber);
                setupResponse = await _rtspTransportClient.EnsureExecuteRequest(setupRequest, token);
            }

            string transportHeader = setupResponse.Headers[WellKnownHeaders.Transport];

            if (string.IsNullOrEmpty(transportHeader))
            {
                throw new RtspBadResponseException("Transport header is not found");
            }

            string portsAttributeName = _connectionParameters.RtpTransport == RtpTransportProtocol.UDP
                ? "server_port"
                : "interleaved";

            string[] transportAttributes = transportHeader.Split(TransportAttributesSeparator, StringSplitOptions.RemoveEmptyEntries);

            string portsAttribute = transportAttributes.FirstOrDefault(a => a.StartsWith(portsAttributeName, StringComparison.InvariantCultureIgnoreCase));

            if (portsAttribute == null || !TryParseSeverPorts(portsAttribute, out rtpChannelNumber, out rtcpChannelNumber))
            {
                throw new RtspBadResponseException("Server ports are not found");
            }

            if (_connectionParameters.RtpTransport == RtpTransportProtocol.UDP)
            {
                string sourceAttribute = transportAttributes.FirstOrDefault(a => a.StartsWith("source", StringComparison.InvariantCultureIgnoreCase));
                int    equalSignIndex;

                IPAddress sourceAddress;

                if (sourceAttribute != null && (equalSignIndex = sourceAttribute.IndexOf("=", StringComparison.CurrentCultureIgnoreCase)) != -1)
                {
                    sourceAddress = IPAddress.Parse(sourceAttribute.Substring(++equalSignIndex).Trim());
                }
                else
                {
                    sourceAddress = ((IPEndPoint)_rtspTransportClient.RemoteEndPoint).Address;
                }

                Debug.Assert(rtpClient != null, nameof(rtpClient) + " != null");
                rtpClient.Connect(new IPEndPoint(sourceAddress, rtpChannelNumber));
                Debug.Assert(rtcpClient != null, nameof(rtcpClient) + " != null");
                rtcpClient.Connect(new IPEndPoint(sourceAddress, rtcpChannelNumber));

                var udpHolePunchingPacketSegment = new ArraySegment <byte>(Array.Empty <byte>());

                await rtpClient.SendAsync(udpHolePunchingPacketSegment, SocketFlags.None);

                await rtcpClient.SendAsync(udpHolePunchingPacketSegment, SocketFlags.None);

                _udpClientsMap[rtpChannelNumber]  = rtpClient;
                _udpClientsMap[rtcpChannelNumber] = rtcpClient;
            }

            ParseSessionHeader(setupResponse.Headers[WellKnownHeaders.Session]);

            IMediaPayloadParser mediaPayloadParser = MediaPayloadParser.CreateFrom(track.Codec);

            IRtpSequenceAssembler rtpSequenceAssembler;

            if (_connectionParameters.RtpTransport == RtpTransportProtocol.TCP)
            {
                rtpSequenceAssembler = null;
                mediaPayloadParser.FrameGenerated = OnFrameGeneratedLockfree;
            }
            else
            {
                rtpSequenceAssembler = new RtpSequenceAssembler(Constants.UdpReceiveBufferSize, 256);
                mediaPayloadParser.FrameGenerated = OnFrameGeneratedThreadSafe;
            }

            var rtpStream = new RtpStream(mediaPayloadParser, track.SamplesFrequency, rtpSequenceAssembler);

            _streamsMap.Add(rtpChannelNumber, rtpStream);

            var rtcpStream = new RtcpStream();

            rtcpStream.SessionShutdown += (sender, args) => _serverCancellationTokenSource.Cancel();
            _streamsMap.Add(rtcpChannelNumber, rtcpStream);

            uint senderSyncSourceId = (uint)_random.Next();

            var rtcpReportsProvider = new RtcpReceiverReportsProvider(rtpStream, rtcpStream, senderSyncSourceId);

            _reportProvidersMap.Add(rtpChannelNumber, rtcpReportsProvider);
        }
        private async Task SetupTrackAsync(RtspMediaTrackInfo track, CancellationToken token)
        {
            RtspRequestMessage  setupRequest;
            RtspResponseMessage setupResponse;

            int    rtpChannelNumber;
            int    rtcpChannelNumber;
            Socket rtpClient  = null;
            Socket rtcpClient = null;

            if (_connectionParameters.RtpTransport == RtpTransportProtocol.UDP)
            {
                rtpClient  = NetworkClientFactory.CreateUdpClient();
                rtcpClient = NetworkClientFactory.CreateUdpClient();

                try
                {
                    IPEndPoint endPoint = new IPEndPoint(IPAddress.Any, 0);
                    rtpClient.Bind(endPoint);

                    int rtpPort = ((IPEndPoint)rtpClient.LocalEndPoint).Port;

                    endPoint = new IPEndPoint(IPAddress.Any, rtpPort + 1);

                    try
                    {
                        rtcpClient.Bind(endPoint);
                    }
                    catch (SocketException e) when(e.SocketErrorCode == SocketError.AddressAlreadyInUse)
                    {
                        endPoint = new IPEndPoint(IPAddress.Any, 0);
                        rtcpClient.Bind(endPoint);
                    }

                    int rtcpPort = ((IPEndPoint)rtcpClient.LocalEndPoint).Port;

                    setupRequest = _requestMessageFactory.CreateSetupUdpUnicastRequest(track.TrackName,
                                                                                       rtpPort, rtcpPort);
                    setupResponse = await _rtspTransportClient.EnsureExecuteRequest(setupRequest, token);
                }
                catch
                {
                    rtpClient.Close();
                    rtcpClient.Close();
                    throw;
                }
            }
            else
            {
                int channelCounter = _streamsMap.Count;
                rtpChannelNumber  = channelCounter;
                rtcpChannelNumber = ++channelCounter;

                setupRequest = _requestMessageFactory.CreateSetupTcpInterleavedRequest(track.TrackName,
                                                                                       rtpChannelNumber, rtcpChannelNumber);
                setupResponse = await _rtspTransportClient.EnsureExecuteRequest(setupRequest, token);
            }

            string transportHeader = setupResponse.Headers[WellKnownHeaders.Transport];

            if (string.IsNullOrEmpty(transportHeader))
            {
                throw new RtspBadResponseException("Transport header is not found");
            }

            string attributeName = _connectionParameters.RtpTransport == RtpTransportProtocol.UDP
                ? "server_port"
                : "interleaved";

            if (!ParseSeverPorts(transportHeader, attributeName, out rtpChannelNumber, out rtcpChannelNumber))
            {
                throw new RtspBadResponseException("Server ports are not found");
            }

            if (_connectionParameters.RtpTransport == RtpTransportProtocol.UDP)
            {
                IPEndPoint remoteEndPoint = (IPEndPoint)_rtspTransportClient.RemoteEndPoint;

                rtpClient?.Connect(new IPEndPoint(remoteEndPoint.Address, rtpChannelNumber));
                rtcpClient?.Connect(new IPEndPoint(remoteEndPoint.Address, rtcpChannelNumber));

                var udpHolePunchingPacketSegment = new ArraySegment <byte>(Array.Empty <byte>());

                await rtpClient.SendAsync(udpHolePunchingPacketSegment, SocketFlags.None);

                await rtcpClient.SendAsync(udpHolePunchingPacketSegment, SocketFlags.None);

                _udpClientsMap[rtpChannelNumber]  = rtpClient;
                _udpClientsMap[rtcpChannelNumber] = rtcpClient;
            }

            ParseSessionHeader(setupResponse.Headers[WellKnownHeaders.Session]);

            IMediaPayloadParser mediaPayloadParser = MediaPayloadParser.CreateFrom(track.Codec);

            IRtpSequenceAssembler rtpSequenceAssembler;

            if (_connectionParameters.RtpTransport == RtpTransportProtocol.TCP)
            {
                rtpSequenceAssembler = null;
                mediaPayloadParser.FrameGenerated = OnFrameGeneratedLockfree;
            }
            else
            {
                rtpSequenceAssembler = new RtpSequenceAssembler(Constants.UdpReceiveBufferSize, 8);
                mediaPayloadParser.FrameGenerated = OnFrameGeneratedThreadSafe;
            }

            var rtpStream = new RtpStream(mediaPayloadParser, track.SamplesFrequency, rtpSequenceAssembler);

            _streamsMap.Add(rtpChannelNumber, rtpStream);

            var rtcpStream = new RtcpStream();

            rtcpStream.SessionShutdown += (sender, args) => _serverCancellationTokenSource.Cancel();
            _streamsMap.Add(rtcpChannelNumber, rtcpStream);

            uint senderSyncSourceId = (uint)_random.Next();

            var rtcpReportsProvider = new RtcpReceiverReportsProvider(rtpStream, rtcpStream, senderSyncSourceId);

            _reportProvidersMap.Add(rtpChannelNumber, rtcpReportsProvider);
        }