/// <summary> /// The SETUP request for a URI specifies the transport mechanism to be /// used for the streamed media. A client can issue a SETUP request for a /// stream that is already playing to change transport parameters, which /// a server MAY allow. If it does not allow this, it MUST respond with /// error "455 Method Not Valid In This State". For the benefit of any /// intervening firewalls, a client must indicate the transport /// parameters even if it has no influence over these parameters, for /// example, where the server advertises a fixed multicast address. /// /// https://tools.ietf.org/html/rfc2326#section-10.4 /// </summary> public async Task <RtspSetupResponse> SetupAsync(Uri rtspControlUri, int rtpPort, int rtcpPort) { var requestMessage = new RtspRequestMessage(rtspControlUri, "SETUP") { Headers = { new KeyValuePair <string, string>("Transport", $"RTP/AVP;unicast;client_port={rtpPort}-{rtcpPort}") } }; var response = await SendAsync(requestMessage); var sessionHeader = response.Headers.Get("Session"); var transportHeader = response.Headers.Get("Transport"); var sessionKeyValues = KeyValueParser.ParsePairs(sessionHeader, ';'); var session = sessionKeyValues[0].Key; var sessionTimeout = sessionKeyValues.FirstOrDefault(l => l.Key == "timeout").Value; var transportKeyValues = KeyValueParser.ParsePairs(transportHeader, ';'); var serverPortRange = transportKeyValues.FirstOrDefault(l => l.Key == "server_port").Value; var ssrc = transportKeyValues.FirstOrDefault(l => l.Key == "ssrc").Value; return(new RtspSetupResponse { Session = session, SessionTimeoutSeconds = int.Parse(sessionTimeout), ServerPorts = serverPortRange?.Split('-').Select(int.Parse).ToArray(), Ssrc = Convert.ToUInt32(ssrc, 16), ResponseMessage = response }); }
/// <summary> /// The PLAY method tells the server to start sending data via the /// mechanism specified in SETUP. A client MUST NOT issue a PLAY request /// until any outstanding SETUP requests have been acknowledged as /// successful. /// /// The PLAY request positions the normal play time to the beginning of /// the range specified and delivers stream data until the end of the /// range is reached. PLAY requests may be pipelined (queued); a server /// MUST queue PLAY requests to be executed in order. That is, a PLAY /// request arriving while a previous PLAY request is still active is /// delayed until the first has been completed. /// /// This allows precise editing. /// /// https://tools.ietf.org/html/rfc2326#section-10.5 /// </summary> /// /// <param name="session"> /// Session identifiers are opaque strings of arbitrary length. Linear /// white space must be URL-escaped. A session identifier MUST be chosen /// randomly and MUST be at least eight octets long to make guessing it /// more difficult. (See Section 16.) /// /// session-id = 1*( ALPHA | DIGIT | safe ) /// /// https://tools.ietf.org/html/rfc2326#section-3.4 /// </param> /// /// <param name="nptRange"> /// Normal play time (NPT) indicates the stream absolute position /// relative to the beginning of the presentation. The timestamp consists /// of a decimal fraction. The part left of the decimal may be expressed /// in either seconds or hours, minutes, and seconds. The part right of /// the decimal point measures fractions of a second. /// /// The beginning of a presentation corresponds to 0.0 seconds. Negative /// values are not defined. The special constant now is defined as the /// current instant of a live event. It may be used only for live events. /// /// NPT is defined as in DSM-CC: "Intuitively, NPT is the clock the /// viewer associates with a program. It is often digitally displayed on /// a VCR. NPT advances normally when in normal play mode (scale = 1), /// advances at a faster rate when in fast scan forward (high positive /// scale ratio), decrements when in scan reverse (high negative scale /// ratio) and is fixed in pause mode. NPT is (logically) equivalent to /// SMPTE time codes." [5] /// /// npt-range = ( npt-time "-" [ npt-time ] ) | ( "-" npt-time ) /// npt-time = "now" | npt-sec | npt-hhmmss /// npt-sec = 1*DIGIT [ "." *DIGIT ] /// npt-hhmmss = npt-hh ":" npt-mm ":" npt-ss [ "." *DIGIT ] /// npt-hh = 1*DIGIT ; any positive number /// npt-mm = 1*2DIGIT ; 0-59 /// npt-ss = 1*2DIGIT ; 0-59 /// /// Examples: /// npt=123.45-125 /// npt=12:05:35.3- /// npt=now- /// /// The syntax conforms to ISO 8601. The npt-sec notation is optimized /// for automatic generation, the ntp-hhmmss notation for consumption /// by human readers. The "now" constant allows clients to request to /// receive the live feed rather than the stored or time-delayed /// version. This is needed since neither absolute time nor zero time /// are appropriate for this case. /// /// https://tools.ietf.org/html/rfc2326#section-3.6 /// </param> public async Task <RtspPlayResponse> PlayAsync(Uri rtspUri, string session, string nptRange) { var requestMessage = new RtspRequestMessage(rtspUri, "PLAY") { Headers = { new KeyValuePair <string, string>("Session", session), new KeyValuePair <string, string>("Range", $"npt={nptRange}") } }; var response = await SendAsync(requestMessage); var rtpInfoHeader = response.Headers.Get("RTP-Info"); var rtpInfoParts = rtpInfoHeader.Split(','); var rtpInfos = new List <RtpInfo>(); foreach (var rtpInfoPart in rtpInfoParts) { var pairs = KeyValueParser.ParsePairs(rtpInfoPart, ';'); var rtpInfo = new RtpInfo { Url = pairs.First(l => l.Key == "url").Value, Seq = Convert.ToUInt32(pairs.First(l => l.Key == "seq").Value), RtpTime = Convert.ToUInt32(pairs.FirstOrDefault(l => l.Key == "rtptime").Value) }; rtpInfos.Add(rtpInfo); } return(new RtspPlayResponse { RtpInfo = rtpInfos.ToArray(), ResponseMessage = response }); }