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