예제 #1
0
        public async Task ConnectAsync(DateTime initialTimeStamp, CancellationToken token)
        {
            IRtspTransportClient rtspTransportClient = _transportClientProvider();

            Volatile.Write(ref _rtspTransportClient, rtspTransportClient);

            await _rtspTransportClient.ConnectAsync(token);

            RtspRequestMessage  optionsRequest  = _requestMessageFactory.CreateOptionsRequest();
            RtspResponseMessage optionsResponse = await _rtspTransportClient.ExecuteRequest(optionsRequest, token);

            if (optionsResponse.StatusCode == RtspStatusCode.Ok)
            {
                ParsePublicHeader(optionsResponse.Headers[WellKnownHeaders.Public]);
            }

            RtspRequestMessage  describeRequest  = _requestMessageFactory.CreateDescribeRequest();
            RtspResponseMessage describeResponse =
                await _rtspTransportClient.EnsureExecuteRequest(describeRequest, token);

            string contentBaseHeader = describeResponse.Headers[WellKnownHeaders.ContentBase];

            if (!string.IsNullOrEmpty(contentBaseHeader))
            {
                _requestMessageFactory.ContentBase = new Uri(contentBaseHeader);
            }

            var parser = new SdpParser();
            IEnumerable <RtspTrackInfo> tracks = parser.Parse(describeResponse.ResponseBody);

            bool anyTrackRequested = false;

            foreach (RtspMediaTrackInfo track in GetTracksToSetup(tracks))
            {
                await SetupTrackAsync(initialTimeStamp, track, token);

                anyTrackRequested = true;
            }

            if (!anyTrackRequested)
            {
                throw new RtspClientException("Any suitable track is not found");
            }

            RtspRequestMessage  playRequest  = (initialTimeStamp != default(DateTime) ? _requestMessageFactory.CreatePlayRequest(initialTimeStamp) : _requestMessageFactory.CreatePlayRequest());
            RtspResponseMessage playResponse =
                await _rtspTransportClient.EnsureExecuteRequest(playRequest, token, 1);

            // TODO : Create a specific parse to convert the clock values
            Regex clockRegex = new Regex(@"clock=(?<startTime>\d{8}T\d{6}Z)\-(?<endTime>\d{8}T\d{6}Z)", RegexOptions.Singleline);

            foreach (string playResponseHeader in playResponse.Headers.GetValues("Range"))
            {
                Match clockMatches = clockRegex.Match(playResponseHeader);
                if (clockMatches.Success)
                {
                    _mediaPayloadParser.BaseTime = DateTime.ParseExact(clockMatches.Groups["startTime"].Value, "yyyyMMddTHHmmssZ", CultureInfo.InvariantCulture, DateTimeStyles.None);
                }
            }
        }
        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);
        }
예제 #3
0
        private async Task ProcessSelectIceCandidateAsync(string iceUserLocal, WSPayload clientReq)
        {
            var sdpParser = new SdpParser();
            var clientSdp = sdpParser.Parse(clientReq.sdp);

            var iceCandidate = clientSdp.MediaDescriptions
                               .SelectMany(l => l.IceCandidates)
                               .FirstOrDefault(l => l.CandidateType == "host");

            var iceAttributes = clientSdp.MediaDescriptions
                                .Select(l => l.IceAttributes)
                                .FirstOrDefault();

            if (iceCandidate != null && iceAttributes != null)
            {
                try
                {
                    var iceClientIps = await Dns.GetHostEntryAsync(iceCandidate.ConnectionAddress);

                    var iceClientIp       = iceClientIps.AddressList.First();
                    var clientIceEndpoint = new IPEndPoint(iceClientIp, iceCandidate.Port);

                    await SendIceCandidateAsync(clientIceEndpoint, iceUserLocal, iceCandidate, iceAttributes);
                }
                catch (SocketException e) when(e.SocketErrorCode == SocketError.HostNotFound)
                {
                    _logger.LogInformation("Unable to resolve ice host {ConnectionAddress} to an IP Address", iceCandidate.ConnectionAddress);
                }
            }
        }
        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 async Task ConnectAsync(CancellationToken token)
        {
            IRtspTransportClient rtspTransportClient = _transportClientProvider();

            Volatile.Write(ref _rtspTransportClient, rtspTransportClient);

            await _rtspTransportClient.ConnectAsync(token);

            RtspRequestMessage  optionsRequest  = _requestMessageFactory.CreateOptionsRequest();
            RtspResponseMessage optionsResponse = await _rtspTransportClient.ExecuteRequest(optionsRequest, token);

            if (optionsResponse.StatusCode == RtspStatusCode.Ok)
            {
                ParsePublicHeader(optionsResponse.Headers[WellKnownHeaders.Public]);
            }

            RtspRequestMessage  describeRequest  = _requestMessageFactory.CreateDescribeRequest();
            RtspResponseMessage describeResponse =
                await _rtspTransportClient.EnsureExecuteRequest(describeRequest, token);

            string contentBaseHeader = describeResponse.Headers[WellKnownHeaders.ContentBase];

            if (!string.IsNullOrEmpty(contentBaseHeader))
            {
                _requestMessageFactory.ContentBase = new Uri(contentBaseHeader);
            }

            var parser = new SdpParser();
            IEnumerable <RtspTrackInfo> tracks = parser.Parse(describeResponse.ResponseBody);

            bool anyTrackRequested = false;

            foreach (RtspMediaTrackInfo track in GetTracksToSetup(tracks))
            {
                await SetupTrackAsync(track, token);

                Codec             = track.Codec;
                anyTrackRequested = true;
            }

            if (!anyTrackRequested)
            {
                throw new RtspClientException("Any suitable track is not found");
            }

            RtspRequestMessage playRequest = _requestMessageFactory.CreatePlayRequest();
            await _rtspTransportClient.EnsureExecuteRequest(playRequest, token, 1);
        }
        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);
        }
예제 #7
0
        public async Task <RtspStream> PlayStreamAsync(string uri, NetworkCredential credentials = null)
        {
            var rtspStream = new RtspStream(uri, credentials);

            if (rtspStream.Credentials != null)
            {
                _rtspClient.AddCredential(rtspStream.Uri, rtspStream.Credentials);
            }

            var options = await _rtspClient.OptionsAsync(rtspStream.Uri);

            var describeResponse = await _rtspClient.DescribeAsync(rtspStream.Uri);

            var sdpParser = new SdpParser();

            rtspStream.Sdp = sdpParser.Parse(describeResponse.Sdp);

            var contentBaseHeaderValue =
                describeResponse.ResponseMessage.Headers
                .Get("Content-Base") ??
                describeResponse.ResponseMessage.Headers
                .Get("Content-Location");

            var contentBaseUrl = contentBaseHeaderValue != null ? new Uri(contentBaseHeaderValue) : rtspStream.Uri;

            string session = null;

            foreach (var md in rtspStream.Sdp.MediaDescriptions)
            {
                var rtspControlUrl = md.RtspControlUrl.IsAbsoluteUri
                    ? md.RtspControlUrl
                    : new Uri(contentBaseUrl, md.RtspControlUrl);

                var setupResponse = await _rtspClient.SetupAsync(rtspControlUrl, _rtpClient.Port, _rtcpClient.Port);

                md.MediaSourceAttributes.Ssrc = setupResponse.Ssrc;

                session ??= setupResponse.Session;
            }

            rtspStream.Session = session;

            var playResponse = await _rtspClient.PlayAsync(rtspStream.Uri, session, "0.000-");

            return(rtspStream);
        }
        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));
        }