Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 3 additions & 3 deletions RTSP.Tests/RTSP.Tests.csproj
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<TargetFrameworks>net9.0;net472</TargetFrameworks>
<TargetFrameworks>net10.0;net472</TargetFrameworks>
<LangVersion>12.0</LangVersion>
<GenerateAssemblyInfo>true</GenerateAssemblyInfo>
<Nullable>enable</Nullable>
Expand Down Expand Up @@ -34,12 +34,12 @@
<ItemGroup>
<PackageReference Include="NSubstitute" Version="5.3.0" />
<PackageReference Include="NUnit" Version="4.4.0" />
<PackageReference Include="NUnit.Analyzers" Version="4.10.0">
<PackageReference Include="NUnit.Analyzers" Version="4.11.2">
<PrivateAssets>all</PrivateAssets>
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
</PackageReference>
<PackageReference Include="NUnit3TestAdapter" Version="5.2.0" />
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="18.0.0" />
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="18.0.1" />
<PackageReference Include="Meziantou.Polyfill" Version="1.0.62" Condition="'$(TargetFramework)' == 'net472'">
<PrivateAssets>all</PrivateAssets>
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
Expand Down
29 changes: 14 additions & 15 deletions RTSP/HeadersParser.cs
Original file line number Diff line number Diff line change
@@ -1,25 +1,24 @@
using System;
namespace Rtsp;

using System;
using System.Collections.Specialized;
using System.IO;

namespace Rtsp
internal static class HeadersParser
{
internal static class HeadersParser
public static NameValueCollection ParseHeaders(StreamReader headersReader)
{
public static NameValueCollection ParseHeaders(StreamReader headersReader)
NameValueCollection headers = new(StringComparer.InvariantCultureIgnoreCase);
string? header;
while (!string.IsNullOrEmpty(header = headersReader.ReadLine()))
{
NameValueCollection headers = new(StringComparer.InvariantCultureIgnoreCase);
string? header;
while (!string.IsNullOrEmpty(header = headersReader.ReadLine()))
{
int colonPos = header.IndexOf(':', StringComparison.InvariantCulture);
if (colonPos == -1) { continue; }
string key = header[..colonPos].Trim().ToUpperInvariant();
string value = header[++colonPos..].Trim();
int colonPos = header.IndexOf(':', StringComparison.InvariantCulture);
if (colonPos == -1) { continue; }
string key = header[..colonPos].Trim().ToUpperInvariant();
string value = header[++colonPos..].Trim();

headers.Add(key, value);
}
return headers;
headers.Add(key, value);
}
return headers;
}
}
107 changes: 53 additions & 54 deletions RTSP/IRTSPTransport.cs
Original file line number Diff line number Diff line change
@@ -1,60 +1,59 @@
namespace Rtsp;

using System;
using System.Net;

namespace Rtsp
/// <summary>
/// Interface for Transport of Rtsp (TCP, TCP+SSL,..)
/// </summary>
public interface IRtspTransport
{
/// <summary>
/// Interface for Transport of Rtsp (TCP, TCP+SSL,..)
/// </summary>
public interface IRtspTransport
{
/// <summary>
/// Gets the stream of the transport.
/// </summary>
/// <returns>A stream</returns>
System.IO.Stream GetStream();

[Obsolete("Get the address from the RemoteEndPoint instead.")]
/// <summary>
/// Gets the remote address.
/// </summary>
/// <value>The remote address.</value>
/// <remarks>This property actually returns an IP:Port pair or a URI, depending on the underlying transport.</remarks>
string RemoteAddress { get; }

/// <summary>
/// Gets the remote endpoint.
/// </summary>
/// <value>The remote endpoint.</value>
IPEndPoint RemoteEndPoint { get; }

/// <summary>
/// Gets the remote endpoint.
/// </summary>
/// <value>The remote endpoint.</value>
IPEndPoint LocalEndPoint { get; }

/// <summary>
/// Get next command index. Increment at each call.
/// </summary>
uint NextCommandIndex();

/// <summary>
/// Closes this instance.
/// </summary>
void Close();

/// <summary>
/// Gets a value indicating whether this <see cref="IRtspTransport"/> is connected.
/// </summary>
/// <value><see langword="true"/> if connected; otherwise, <see langword="false"/>.</value>
bool Connected { get; }

/// <summary>
/// Reconnect this instance.
/// <remarks>Must do nothing if already connected.</remarks>
/// </summary>
/// <exception cref="System.Net.Sockets.SocketException">Error during socket </exception>
void Reconnect();
}
/// Gets the stream of the transport.
/// </summary>
/// <returns>A stream</returns>
System.IO.Stream GetStream();

[Obsolete("Get the address from the RemoteEndPoint instead.")]
/// <summary>
/// Gets the remote address.
/// </summary>
/// <value>The remote address.</value>
/// <remarks>This property actually returns an IP:Port pair or a URI, depending on the underlying transport.</remarks>
string RemoteAddress { get; }

/// <summary>
/// Gets the remote endpoint.
/// </summary>
/// <value>The remote endpoint.</value>
IPEndPoint RemoteEndPoint { get; }

/// <summary>
/// Gets the remote endpoint.
/// </summary>
/// <value>The remote endpoint.</value>
IPEndPoint LocalEndPoint { get; }

/// <summary>
/// Get next command index. Increment at each call.
/// </summary>
uint NextCommandIndex();

/// <summary>
/// Closes this instance.
/// </summary>
void Close();

/// <summary>
/// Gets a value indicating whether this <see cref="IRtspTransport"/> is connected.
/// </summary>
/// <value><see langword="true"/> if connected; otherwise, <see langword="false"/>.</value>
bool Connected { get; }

/// <summary>
/// Reconnect this instance.
/// <remarks>Must do nothing if already connected.</remarks>
/// </summary>
/// <exception cref="System.Net.Sockets.SocketException">Error during socket </exception>
void Reconnect();
}
41 changes: 20 additions & 21 deletions RTSP/IRtpTransport.cs
Original file line number Diff line number Diff line change
@@ -1,28 +1,27 @@
using System;
namespace Rtsp;

using System;
using System.Threading.Tasks;

namespace Rtsp
public interface IRtpTransport : IDisposable
{
public interface IRtpTransport : IDisposable
{
event EventHandler<RtspDataEventArgs>? DataReceived;
event EventHandler<RtspDataEventArgs>? ControlReceived;
event EventHandler<RtspDataEventArgs>? DataReceived;
event EventHandler<RtspDataEventArgs>? ControlReceived;

void Start();
void Stop();
void Start();
void Stop();

/// <summary>
/// Write to the RTP Control Port
/// </summary>
/// <param name="data">Buffer to send</param>
void WriteToControlPort(ReadOnlySpan<byte> data);
Task WriteToControlPortAsync(ReadOnlyMemory<byte> data);
/// <summary>
/// Write to the RTP Control Port
/// </summary>
/// <param name="data">Buffer to send</param>
void WriteToControlPort(ReadOnlySpan<byte> data);
Task WriteToControlPortAsync(ReadOnlyMemory<byte> data);

/// <summary>
/// Write to the RTP Data Port
/// </summary>
/// <param name="data">Buffer to send</param>
void WriteToDataPort(ReadOnlySpan<byte> data);
Task WriteToDataPortAsync(ReadOnlyMemory<byte> data);
}
/// <summary>
/// Write to the RTP Data Port
/// </summary>
/// <param name="data">Buffer to send</param>
void WriteToDataPort(ReadOnlySpan<byte> data);
Task WriteToDataPortAsync(ReadOnlyMemory<byte> data);
}
7 changes: 2 additions & 5 deletions RTSP/Messages/RTSPData.cs
Original file line number Diff line number Diff line change
Expand Up @@ -25,11 +25,8 @@ public override Memory<byte> Data
get => base.Data;
set
{
if (_reservedData != null)
{
_reservedData.Dispose();
_reservedData = null;
}
_reservedData?.Dispose();
_reservedData = null;
base.Data = value;
}
}
Expand Down
15 changes: 11 additions & 4 deletions RTSP/Messages/RTSPMessage.cs
Original file line number Diff line number Diff line change
Expand Up @@ -9,12 +9,19 @@
using System.Text;
using System.Text.RegularExpressions;

public class RtspMessage : RtspChunk
public partial class RtspMessage : RtspChunk
{
/// <summary>
/// The regex to validate the Rtsp message.
/// </summary>
private static readonly Regex RtspVersionTest = new(@"^RTSP/\d\.\d", RegexOptions.Compiled, TimeSpan.FromMilliseconds(10));
#if NET8_0_OR_GREATER
[GeneratedRegex(@"^RTSP/\d\.\d", RegexOptions.None, 10)]
private static partial Regex RtspVersionTest();
#else
private static readonly Regex _rtspVersionTest = new(@"^RTSP/\d\.\d", RegexOptions.Compiled, TimeSpan.FromMilliseconds(10));
private static Regex RtspVersionTest() => _rtspVersionTest;
#endif


/// <summary>
/// Create the good type of Rtsp Message from the header.
Expand All @@ -34,11 +41,11 @@ public static RtspMessage GetRtspMessage(string aRequestLine)
// A request is : Method SP Request-URI SP RTSP-Version
// A response is : RTSP-Version SP Status-Code SP Reason-Phrase
// RTSP-Version = "RTSP" "/" 1*DIGIT "." 1*DIGIT
if (RtspVersionTest.IsMatch(requestParts[2]))
if (RtspVersionTest().IsMatch(requestParts[2]))
{
returnValue = RtspRequest.GetRtspRequest(requestParts);
}
else if (RtspVersionTest.IsMatch(requestParts[0]))
else if (RtspVersionTest().IsMatch(requestParts[0]))
{
returnValue = new RtspResponse();
}
Expand Down
97 changes: 48 additions & 49 deletions RTSP/MulticastUdpSocket.cs
Original file line number Diff line number Diff line change
@@ -1,67 +1,66 @@
using System;
namespace Rtsp;

using System;
using System.Net;
using System.Net.Sockets;

namespace Rtsp
public class MulticastUDPSocket : UDPSocket
{
public class MulticastUDPSocket : UDPSocket
private readonly IPAddress dataMulticastAddress;
private readonly IPAddress controlMulticastAddress;

/// <summary>
/// Initializes a new instance of the <see cref="UDPSocket"/> class.
/// Used with Multicast mode with the Multicast Address and Port
/// </summary>
public MulticastUDPSocket(string data_multicast_address, int data_multicast_port, string control_multicast_address, int control_multicast_port)
: base(new UdpClient(), new UdpClient())
{
private readonly IPAddress dataMulticastAddress;
private readonly IPAddress controlMulticastAddress;
// open a pair of UDP sockets - one for data (video or audio) and one for the status channel (RTCP messages)
DataPort = data_multicast_port;
ControlPort = control_multicast_port;

/// <summary>
/// Initializes a new instance of the <see cref="UDPSocket"/> class.
/// Used with Multicast mode with the Multicast Address and Port
/// </summary>
public MulticastUDPSocket(string data_multicast_address, int data_multicast_port, string control_multicast_address, int control_multicast_port)
: base(new UdpClient(), new UdpClient())
try
{
// open a pair of UDP sockets - one for data (video or audio) and one for the status channel (RTCP messages)
DataPort = data_multicast_port;
ControlPort = control_multicast_port;

try
{
var dataEndPoint = new IPEndPoint(IPAddress.Any, DataPort);
var controlEndPoint = new IPEndPoint(IPAddress.Any, ControlPort);
var dataEndPoint = new IPEndPoint(IPAddress.Any, DataPort);
var controlEndPoint = new IPEndPoint(IPAddress.Any, ControlPort);

dataMulticastAddress = IPAddress.Parse(data_multicast_address);
controlMulticastAddress = IPAddress.Parse(control_multicast_address);
dataMulticastAddress = IPAddress.Parse(data_multicast_address);
controlMulticastAddress = IPAddress.Parse(control_multicast_address);

dataSocket.Client.Bind(dataEndPoint);
dataSocket.JoinMulticastGroup(dataMulticastAddress);
dataSocket.Client.Bind(dataEndPoint);
dataSocket.JoinMulticastGroup(dataMulticastAddress);

controlSocket.Client.Bind(controlEndPoint);
controlSocket.JoinMulticastGroup(controlMulticastAddress);
controlSocket.Client.Bind(controlEndPoint);
controlSocket.JoinMulticastGroup(controlMulticastAddress);

dataSocket.Client.ReceiveBufferSize = 100 * 1024;
dataSocket.Client.SendBufferSize = 65535; // default is 8192. Make it as large as possible for large RTP packets which are not fragmented
dataSocket.Client.ReceiveBufferSize = 100 * 1024;
dataSocket.Client.SendBufferSize = 65535; // default is 8192. Make it as large as possible for large RTP packets which are not fragmented

controlSocket.Client.DontFragment = false;
}
catch (SocketException)
{
// Fail to allocate port, try again
dataSocket?.Close();
controlSocket?.Close();
throw;
}

if (dataSocket == null || controlSocket == null)
{
throw new InvalidOperationException("UDP Forwader host was not initialized, can't continue");
}
controlSocket.Client.DontFragment = false;
}
catch (SocketException)
{
// Fail to allocate port, try again
dataSocket?.Close();
controlSocket?.Close();
throw;
}

/// <summary>
/// Stops this instance.
/// </summary>
public override void Stop()
if (dataSocket == null || controlSocket == null)
{
// leave the multicast groups
dataSocket.DropMulticastGroup(dataMulticastAddress);
controlSocket.DropMulticastGroup(controlMulticastAddress);
base.Stop();
throw new InvalidOperationException("UDP Forwader host was not initialized, can't continue");
}
}

/// <summary>
/// Stops this instance.
/// </summary>
public override void Stop()
{
// leave the multicast groups
dataSocket.DropMulticastGroup(dataMulticastAddress);
controlSocket.DropMulticastGroup(controlMulticastAddress);
base.Stop();
}
}
Loading