Skip to content

Commit 9a9baf4

Browse files
committed
F Adding FFMpeg streaming
1 parent bfe25ba commit 9a9baf4

7 files changed

Lines changed: 196 additions & 1 deletion

File tree

src/RTSPServerFFmpeg/Program.cs

Lines changed: 71 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,71 @@
1+
using Microsoft.Extensions.Configuration;
2+
using SharpRTSPServer;
3+
using System;
4+
5+
IConfiguration config = new ConfigurationBuilder().AddJsonFile("appsettings.json").Build();
6+
string hostName = config["HostName"];
7+
ushort port = ushort.Parse(config["Port"]);
8+
string userName = config["UserName"];
9+
string password = config["Password"];
10+
11+
// ffmpeg.exe -re -stream_loop -1 -i C:\Git\SharpMediaCoder\src\SharpMediaPlayer\frag_bunny.mp4 -vcodec copy -an -f rtp rtp://127.0.0.1:11111 -vn -acodec copy -f rtp rtp://127.0.0.1:11113
12+
using (var server = new RTSPServer(port, userName, password))
13+
{
14+
server.AddVideoTrack(new ProxyTrack(0, "rtp://127.0.0.1:11111"));
15+
server.AddAudioTrack(new ProxyTrack(1, "rtp://127.0.0.1:11113"));
16+
// original
17+
//string sdp =
18+
// "v=0\r\n" +
19+
// "o=- 0 0 IN IP4 127.0.0.1\r\n" +
20+
// "s=No Name\r\n" +
21+
// "t=0 0\r\n" +
22+
// "a=tool:libavformat 60.3.100\r\n" +
23+
// "m=video 11111 RTP/AVP 96\r\n" +
24+
// "c=IN IP4 127.0.0.1\r\n" +
25+
// "b=AS:587\r\n" +
26+
// "a=rtpmap:96 H264/90000\r\n" +
27+
// "a=fmtp:96 packetization-mode=1; sprop-parameter-sets=Z2QAHqzZQKAv+WagwCDW4AAAAwAgAAAGAeLFssA=,aOvjyyLA; profile-level-id=64001E\r\n" +
28+
// "m=audio 11113 RTP/AVP 97\r\n" +
29+
// "c=IN IP4 127.0.0.1\r\n" +
30+
// "b=AS:69\r\n" +
31+
// "a=rtpmap:97 MPEG4-GENERIC/44100/1\r\n" +
32+
// "a=fmtp:97 profile-level-id=1;mode=AAC-hbr;sizelength=13;indexlength=3;indexdeltalength=3; config=120856E500\r\n\r\n";
33+
34+
string sdp =
35+
"v=0\r\n" +
36+
"o=- 0 0 IN IP4 127.0.0.1\r\n" +
37+
"s=No Name\r\n" +
38+
"t=0 0\r\n" +
39+
"a=tool:libavformat 60.3.100\r\n" +
40+
"m=video 11111 RTP/AVP 96\r\n" +
41+
"a=control:trackID=0\n" + // TODO: add this
42+
"c=IN IP4 127.0.0.1\r\n" +
43+
"b=AS:587\r\n" +
44+
"a=rtpmap:96 H264/90000\r\n" +
45+
"a=fmtp:96 packetization-mode=1; sprop-parameter-sets=Z2QAHqzZQKAv+WagwCDW4AAAAwAgAAAGAeLFssA=,aOvjyyLA; profile-level-id=64001E\r\n" +
46+
"m=audio 11113 RTP/AVP 97\r\n" +
47+
"a=control:trackID=1\n" + // TODO: add this
48+
"c=IN IP4 127.0.0.1\r\n" +
49+
"b=AS:69\r\n" +
50+
"a=rtpmap:97 MPEG4-GENERIC/44100/1\r\n" +
51+
"a=fmtp:97 profile-level-id=1;mode=AAC-hbr;sizelength=13;indexlength=3;indexdeltalength=3; config=120856E500\r\n\r\n";
52+
53+
server.OverrideSDP(sdp);
54+
55+
try
56+
{
57+
server.StartListen();
58+
}
59+
catch (Exception ex)
60+
{
61+
Console.WriteLine(ex.ToString());
62+
}
63+
64+
Console.WriteLine($"RTSP URL is rtsp://{userName}:{password}@{hostName}:{port}");
65+
66+
Console.WriteLine("Press any key to exit");
67+
while (!Console.KeyAvailable)
68+
{
69+
System.Threading.Thread.Sleep(250);
70+
}
71+
}
Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,25 @@
1+
<Project Sdk="Microsoft.NET.Sdk">
2+
3+
<PropertyGroup>
4+
<OutputType>Exe</OutputType>
5+
<TargetFramework>net8.0</TargetFramework>
6+
<ImplicitUsings>disable</ImplicitUsings>
7+
<Nullable>disable</Nullable>
8+
</PropertyGroup>
9+
10+
<ItemGroup>
11+
<Content Include="appsettings.json">
12+
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
13+
</Content>
14+
</ItemGroup>
15+
16+
<ItemGroup>
17+
<PackageReference Include="Microsoft.Extensions.Configuration.Json" Version="8.0.0" />
18+
<PackageReference Include="SharpMp4" Version="0.0.6" />
19+
</ItemGroup>
20+
21+
<ItemGroup>
22+
<ProjectReference Include="..\SharpRTSPServer\SharpRTSPServer.csproj" />
23+
</ItemGroup>
24+
25+
</Project>

src/SharpRTSPClient.sln

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,8 @@ Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "RTSPServerApp", "RTSPServer
1313
EndProject
1414
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Samples", "Samples", "{660C9331-283E-462F-9DB1-1BC2BCB12B2D}"
1515
EndProject
16+
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "RTSPServerFFmpeg", "RTSPServerFFmpeg\RTSPServerFFmpeg.csproj", "{4D660B91-8244-4C49-8A46-E0DA989F5111}"
17+
EndProject
1618
Global
1719
GlobalSection(SolutionConfigurationPlatforms) = preSolution
1820
Debug|Any CPU = Debug|Any CPU
@@ -35,13 +37,18 @@ Global
3537
{A1E94FC9-5F61-41F3-99AC-3C081E1F965B}.Debug|Any CPU.Build.0 = Debug|Any CPU
3638
{A1E94FC9-5F61-41F3-99AC-3C081E1F965B}.Release|Any CPU.ActiveCfg = Release|Any CPU
3739
{A1E94FC9-5F61-41F3-99AC-3C081E1F965B}.Release|Any CPU.Build.0 = Release|Any CPU
40+
{4D660B91-8244-4C49-8A46-E0DA989F5111}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
41+
{4D660B91-8244-4C49-8A46-E0DA989F5111}.Debug|Any CPU.Build.0 = Debug|Any CPU
42+
{4D660B91-8244-4C49-8A46-E0DA989F5111}.Release|Any CPU.ActiveCfg = Release|Any CPU
43+
{4D660B91-8244-4C49-8A46-E0DA989F5111}.Release|Any CPU.Build.0 = Release|Any CPU
3844
EndGlobalSection
3945
GlobalSection(SolutionProperties) = preSolution
4046
HideSolutionNode = FALSE
4147
EndGlobalSection
4248
GlobalSection(NestedProjects) = preSolution
4349
{A98D08A3-1617-4953-AB98-11CFB6159529} = {660C9331-283E-462F-9DB1-1BC2BCB12B2D}
4450
{A1E94FC9-5F61-41F3-99AC-3C081E1F965B} = {660C9331-283E-462F-9DB1-1BC2BCB12B2D}
51+
{4D660B91-8244-4C49-8A46-E0DA989F5111} = {660C9331-283E-462F-9DB1-1BC2BCB12B2D}
4552
EndGlobalSection
4653
GlobalSection(ExtensibilityGlobals) = postSolution
4754
SolutionGuid = {BB335B23-D422-4DDC-9B06-833B6E100A0C}

src/SharpRTSPServer/ProxyTrack.cs

Lines changed: 77 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,77 @@
1+
using System;
2+
using System.Buffers;
3+
using System.Collections.Generic;
4+
using System.Diagnostics;
5+
using System.Net;
6+
using System.Net.Sockets;
7+
using System.Text;
8+
using System.Threading.Tasks;
9+
10+
namespace SharpRTSPServer
11+
{
12+
public class ProxyTrack : TrackBase
13+
{
14+
public override string Codec => "PROXY";
15+
16+
public override int ID { get; set; }
17+
public override int PayloadType { get; set; }
18+
19+
private bool _isReady = false;
20+
public override bool IsReady
21+
{
22+
get
23+
{
24+
return _isReady;
25+
}
26+
}
27+
28+
public Uri Uri { get; }
29+
30+
public ProxyTrack(int id, string uri)
31+
{
32+
this.ID = id;
33+
this.Uri = new Uri(uri, UriKind.Absolute);
34+
Connect(this.Uri);
35+
}
36+
37+
public override StringBuilder BuildSDP(StringBuilder sdp)
38+
{
39+
throw new NotImplementedException();
40+
}
41+
42+
public override (List<Memory<byte>>, List<IMemoryOwner<byte>>) CreateRtpPackets(List<byte[]> samples, uint rtpTimestamp)
43+
{
44+
throw new NotImplementedException();
45+
}
46+
47+
public override void FeedInRawSamples(uint rtpTimestamp, List<byte[]> samples)
48+
{
49+
throw new InvalidOperationException("Proxy cannot feed in any samples!");
50+
}
51+
52+
private async void Connect(Uri uri)
53+
{
54+
await Task.Run(() =>
55+
{
56+
UdpClient udpClient = new UdpClient(uri.Port);
57+
IPEndPoint remoteEndPoint = new IPEndPoint(IPAddress.Parse(uri.Host), 0);
58+
_isReady = true;
59+
60+
while (true)
61+
{
62+
try
63+
{
64+
byte[] rtp = udpClient.Receive(ref remoteEndPoint);
65+
uint rtpTimestamp = RTPPacketUtil.ReadRtpTimestamp(rtp);
66+
Sink.FeedInRawRTP(ID, rtpTimestamp, new List<Memory<byte>>() { rtp });
67+
//Debug.WriteLine($"This message was sent from {remoteEndPoint.Address} on their port number {remoteEndPoint.Port}");
68+
}
69+
catch (Exception e)
70+
{
71+
Debug.WriteLine(e.ToString());
72+
}
73+
}
74+
});
75+
}
76+
}
77+
}

src/SharpRTSPServer/RTPPacketUtil.cs

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,12 @@ public static class RTPPacketUtil
77
{
88
public const int RTP_VERSION = 2;
99

10+
public static uint ReadRtpTimestamp(byte[] data)
11+
{
12+
uint rtpTimestamp = ((uint)data[4] << 24) + (uint)(data[5] << 16) + (uint)(data[6] << 8) + data[7];
13+
return rtpTimestamp;
14+
}
15+
1016
public static void WriteHeader(
1117
Span<byte> rtpPacket,
1218
int rtpVersion,

src/SharpRTSPServer/RTSPServer.cs

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -64,6 +64,7 @@ public class RTSPServer : IRtpSender, IDisposable
6464
private CancellationTokenSource _stopping;
6565
private Thread _listenTread;
6666
private int _sessionHandle = 1;
67+
private string _sdp = null;
6768
private readonly NetworkCredential _credentials;
6869
private readonly Authentication _authentication;
6970

@@ -496,6 +497,9 @@ private void HandleDescribe(RtspListener listener, RtspRequest message)
496497

497498
private string GenerateSDP()
498499
{
500+
if(!string.IsNullOrEmpty(_sdp))
501+
return _sdp; // sdp
502+
499503
StringBuilder sdp = new StringBuilder();
500504

501505
// Generate the SDP
@@ -742,6 +746,11 @@ protected virtual void Dispose(bool disposing)
742746
}
743747
}
744748

749+
public void OverrideSDP(string sdp)
750+
{
751+
this._sdp = sdp;
752+
}
753+
745754
#endregion // IDisposable
746755

747756
/// <summary>

src/SharpRTSPServer/TrackBase.cs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -24,7 +24,7 @@ public abstract class TrackBase : ITrack
2424

2525
public abstract (List<Memory<byte>>, List<IMemoryOwner<byte>>) CreateRtpPackets(List<byte[]> samples, uint rtpTimestamp);
2626

27-
public void FeedInRawSamples(uint rtpTimestamp, List<byte[]> samples)
27+
public virtual void FeedInRawSamples(uint rtpTimestamp, List<byte[]> samples)
2828
{
2929
if (Sink == null)
3030
throw new InvalidOperationException("Sink is null!!!");

0 commit comments

Comments
 (0)