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