Got an RTSP Client that works in theory.
This commit is contained in:
parent
1a3453059f
commit
4f1c0c8b10
@ -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)
|
||||
|
||||
@ -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);
|
||||
|
||||
64
skyscraper8/SatIp/RtspRequests/RtspPlayRequest.cs
Normal file
64
skyscraper8/SatIp/RtspRequests/RtspPlayRequest.cs
Normal 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"];
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
57
skyscraper8/SatIp/RtspRequests/RtspSetupRequest.cs
Normal file
57
skyscraper8/SatIp/RtspRequests/RtspSetupRequest.cs
Normal 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);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
51
skyscraper8/SatIp/RtspRequests/RtspTeardownRequest.cs
Normal file
51
skyscraper8/SatIp/RtspRequests/RtspTeardownRequest.cs
Normal 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"]);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
47
skyscraper8/SatIp/RtspResponses/RtspPlayResponse.cs
Normal file
47
skyscraper8/SatIp/RtspResponses/RtspPlayResponse.cs
Normal 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"];
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
112
skyscraper8/SatIp/RtspResponses/RtspSetupResponse.cs
Normal file
112
skyscraper8/SatIp/RtspResponses/RtspSetupResponse.cs
Normal 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);
|
||||
}
|
||||
35
skyscraper8/SatIp/RtspResponses/RtspTeardownResponse.cs
Normal file
35
skyscraper8/SatIp/RtspResponses/RtspTeardownResponse.cs
Normal 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"]);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
Loading…
x
Reference in New Issue
Block a user