Got an RTSP Client that works in theory.

This commit is contained in:
feyris-tan 2025-08-18 22:34:18 +02:00
parent 1a3453059f
commit 4f1c0c8b10
8 changed files with 451 additions and 0 deletions

View File

@ -42,6 +42,23 @@ namespace skyscraper5
string url = RtspClient.MakeUrl(DiSEqC_Opcode.DISEQC_OPTION_A | DiSEqC_Opcode.DISEQC_POSITION_A | DiSEqC_Opcode.DISEQC_HORIZONTAL, 11954, true, 27500);
RtspDescribeResponse describe = rtspClient.GetDescribe(url);
SessionDescriptionProtocol sessionDescriptionProtocol = describe.GetSessionDescriptionProtocol();
int rtcps = 0;
int rtps = 0;
RtspSetupResponse setup = rtspClient.GetSetup(url);
setup.OnRtcpPacket += ((data, length) =>
rtcps++);
setup.OnRtpPacket += (data, length) =>
rtps++;
RtspPlayResponse play = rtspClient.GetPlay(setup);
Thread.Sleep(5000);
rtspClient.GetTeardown(setup);
Console.WriteLine("{0} RTCPs",rtcps);
Console.WriteLine("{0} RTPs",rtps);
}
static void Main(string[] args)

View File

@ -87,6 +87,74 @@ namespace skyscraper8.SatIp
return result;
}
public RtspSetupResponse GetSetup(string url)
{
RtspSetupRequest request = new RtspSetupRequest();
request.RequestPath = url;
request.CSeq = cseqCounter++;
request.UserAgent = USER_AGENT;
Socket rtpSocket = new Socket(AddressFamily.InterNetwork, SocketType.Dgram, ProtocolType.Udp);
rtpSocket.Bind(new IPEndPoint(ListenIp, 0));
int rtpPort = GetPortFromEndPoint(rtpSocket.LocalEndPoint);
Socket rtcpSocket = new Socket(AddressFamily.InterNetwork, SocketType.Dgram, ProtocolType.Udp);
rtcpSocket.Bind(new IPEndPoint(ListenIp, 0));
int rtcpPort = GetPortFromEndPoint(rtcpSocket.LocalEndPoint);
request.SetRtpAvpUnicast(rtpPort, rtcpPort);
RtspResponseHeader response = GetResponse(request.ListHeaders(RootPath));
RtspSetupResponse setupResponse = new RtspSetupResponse(response);
if (response.statusCode == 200)
{
setupResponse.RtpSocket = rtpSocket;
setupResponse.RtcpSocket = rtcpSocket;
setupResponse.SetupListeners();
setupResponse.Valid = true;
}
else
{
setupResponse.Valid = false;
throw new NotImplementedException(setupResponse.RtspStatusCode.ToString());
}
return setupResponse;
}
public RtspPlayResponse GetPlay(RtspSetupResponse setupData)
{
RtspPlayRequest request = new RtspPlayRequest();
request.RequestPath = string.Format("/stream={0}", setupData.StreamId);
request.Session = setupData.Session;
request.UserAgent = USER_AGENT;
RtspPlayResponse response = new RtspPlayResponse(GetResponse(request.ListHeaders(RootPath)));
return response;
}
public RtspTeardownResponse GetTeardown(RtspSetupResponse setupData)
{
RtspTeardownRequest request = new RtspTeardownRequest();
request.RequestPath = string.Format("/stream={0}", setupData.StreamId);
request.Session = setupData.Session;
request.UserAgent = USER_AGENT;
RtspTeardownResponse response = new RtspTeardownResponse(GetResponse(request.ListHeaders(RootPath)));
if (response.RtspStatusCode == 200)
{
setupData.InvokeCancellationTokens();
}
return response;
}
private int GetPortFromEndPoint(EndPoint endpoint)
{
IPEndPoint ipEndPoint = endpoint as IPEndPoint;
if (ipEndPoint == null)
throw new NotImplementedException(endpoint.GetType().Name);
return ipEndPoint.Port;
}
private RtspResponseHeader GetResponse(string request)
{
StreamWriter.Write(request);

View File

@ -0,0 +1,64 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace skyscraper8.SatIp.RtspRequests
{
internal class RtspPlayRequest : RtspRequest
{
public RtspPlayRequest() : base("PLAY")
{
Range = "npt=0.000-";
}
public uint CSeq
{
set
{
base.args["CSeq"] = Convert.ToString(value);
}
get
{
return uint.Parse(base.args["CSeq"]);
}
}
public string UserAgent
{
set
{
base.args["User-Agent"] = value;
}
get
{
return base.args["User-Agent"];
}
}
public uint Session
{
set
{
base.args["Session"] = value.ToString();
}
get
{
return uint.Parse(base.args["Session"]);
}
}
public string Range
{
set
{
base.args["Range"] = value;
}
get
{
return base.args["Range"];
}
}
}
}

View File

@ -0,0 +1,57 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace skyscraper8.SatIp.RtspRequests
{
internal class RtspSetupRequest : RtspRequest
{
public RtspSetupRequest() : base("SETUP")
{
}
public uint CSeq
{
set
{
base.args["CSeq"] = Convert.ToString(value);
}
get
{
return uint.Parse(base.args["CSeq"]);
}
}
public string UserAgent
{
set
{
base.args["User-Agent"] = value;
}
get
{
return base.args["User-Agent"];
}
}
public string Transport
{
set
{
base.args["Transport"] = value;
}
get
{
return base.args["Transport"];
}
}
public void SetRtpAvpUnicast(int rtpPort, int rtcpPort)
{
Transport = String.Format("RTP/AVP;unicast;client_port={0}-{1}", rtpPort, rtcpPort);
}
}
}

View File

@ -0,0 +1,51 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace skyscraper8.SatIp.RtspRequests
{
internal class RtspTeardownRequest : RtspRequest
{
public RtspTeardownRequest() : base("TEARDOWN")
{
}
public uint CSeq
{
set
{
base.args["CSeq"] = Convert.ToString(value);
}
get
{
return uint.Parse(base.args["CSeq"]);
}
}
public string UserAgent
{
set
{
base.args["User-Agent"] = value;
}
get
{
return base.args["User-Agent"];
}
}
public uint Session
{
set
{
base.args["Session"] = value.ToString();
}
get
{
return uint.Parse(base.args["Session"]);
}
}
}
}

View File

@ -0,0 +1,47 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace skyscraper8.SatIp.RtspResponses
{
internal class RtspPlayResponse : RtspResponse
{
public RtspPlayResponse(RtspResponseHeader header) : base(header)
{
}
public uint CSeq
{
get
{
return uint.Parse(base.args["CSeq"]);
}
}
public uint Session
{
set
{
base.args["Session"] = value.ToString();
}
get
{
return uint.Parse(base.args["Session"]);
}
}
public string RtpInfo
{
set
{
base.args["RTP-Info"] = value.ToString();
}
get
{
return base.args["RTP-Info"];
}
}
}
}

View File

@ -0,0 +1,112 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Net.Sockets;
using System.Text;
using System.Threading.Tasks;
namespace skyscraper8.SatIp.RtspResponses
{
internal class RtspSetupResponse : RtspResponse
{
public RtspSetupResponse(RtspResponseHeader header) : base(header)
{
}
public Socket RtpSocket { get; set; }
public Socket RtcpSocket { get; set; }
public uint CSeq
{
get
{
return uint.Parse(base.args["CSeq"]);
}
}
public uint Session
{
get
{
string s = base.args["Session"];
string[] strings = s.Split(';');
return uint.Parse(strings[0]);
}
}
public uint StreamId
{
get
{
return uint.Parse(base.args["com.ses.streamID"]);
}
}
private byte exitedThread;
private CancellationTokenSource rtpCancellation;
private CancellationTokenSource rtcpCancellation;
private Thread rtpThread;
private Thread rtcpThread;
private bool listenersStarted;
internal void SetupListeners()
{
if (listenersStarted)
throw new RtspException("Listener already started.");
rtpCancellation = new CancellationTokenSource();
rtcpCancellation = new CancellationTokenSource();
rtpThread = new Thread(RtpThread);
rtcpThread = new Thread(RtcpThread);
rtpThread.Start();
rtcpThread.Start();
listenersStarted = true;
}
private void RtpThread()
{
Task.Run(async () =>
{
byte[] buffer = new byte[2048];
while (!rtpCancellation.IsCancellationRequested)
{
Array.Clear(buffer);
int result = await RtpSocket.ReceiveAsync(buffer, rtpCancellation.Token);
OnRtpPacket?.Invoke(buffer, result);
}
exitedThread++;
}
);
}
private void RtcpThread()
{
Task.Run(async () =>
{
byte[] buffer = new byte[2048];
while (!rtcpCancellation.IsCancellationRequested)
{
Array.Clear(buffer);
ValueTask<int> task = RtcpSocket.ReceiveAsync(buffer, rtcpCancellation.Token);
int taskResult = task.Result;
OnRtcpPacket?.Invoke(buffer, taskResult);
}
exitedThread++;
}
);
}
internal void InvokeCancellationTokens()
{
rtpCancellation.Cancel();
rtcpCancellation.Cancel();
}
public event OnRtpPacket OnRtpPacket;
public event OnRtpPacket OnRtcpPacket;
}
public delegate void OnRtpPacket(byte[] data, int length);
}

View File

@ -0,0 +1,35 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace skyscraper8.SatIp.RtspResponses
{
internal class RtspTeardownResponse : RtspResponse
{
public RtspTeardownResponse(RtspResponseHeader header) : base(header)
{
}
public uint CSeq
{
get
{
return uint.Parse(base.args["CSeq"]);
}
}
public uint Session
{
set
{
base.args["Session"] = value.ToString();
}
get
{
return uint.Parse(base.args["Session"]);
}
}
}
}