Simple RTSP Server
The SimpleRtspServer sample demonstrates how to use RtspServer in stand-alone mode with manual media flow handling. Unlike the Multi-Protocol Server where StreamingServer manages media routing automatically, this sample handles publisher/client connections, stream registration, and media sample forwarding directly in user code.
This approach is useful when you need full control over the media pipeline — for example, custom routing logic, per-client filtering, or integration with external processing systems.
Overview
The SimpleRtspServer class performs the following:
- Creates an RtspServer with configurable parameters
- Subscribes to connection events and manages publisher/client dictionaries
- Forwards media samples from publishers to all connected clients on the same stream
- Optionally supports pull sources (RTSP client) and image sources as server-side publishers
Initialization
Server Parameters
var pars = new VAST.RTSP.RtspServerParameters();
pars.AllowedRtpTransports = VAST.RTP.RtpTransportType.Any;
pars.RtspEndPoints.Add(new IPEndPoint(IPAddress.Any, 554));
| Parameter | Value | Description |
|---|---|---|
AllowedRtpTransports |
Any |
Accept all RTP transport types (UDP, TCP interleaved, multicast) |
RtspEndPoints |
Port 554 | RTSP listening endpoints |
For RTSPS (secure), add an RTSPS endpoint and certificate:
pars.RtspsEndPoints.Add(new IPEndPoint(IPAddress.Any, 322));
pars.CertificateThumbprint = "YOUR_CERTIFICATE_THUMBPRINT";
Creating the Server
this.server = new VAST.RTSP.RtspServer(100, pars);
this.server.Connected += Server_Connected;
this.server.Disconnected += Server_Disconnected;
this.server.PublisherConnected += Server_PublisherConnected;
this.server.ClientConnected += Server_ClientConnected;
this.server.Start();
The first constructor parameter (100) sets the maximum number of concurrent connections. RtspServer exposes four events for connection lifecycle management.
Internal State
The sample tracks connections using dictionaries:
Dictionary<string, PublishedStream> publishedStreams;
Dictionary<EndPoint, PublishedStream> connectedPublishers;
Dictionary<EndPoint, PublishedStream> connectedClients;
Each PublishedStream holds the stream name, media type descriptors, and a dictionary of connected client sinks:
class PublishedStream
{
public string PublishName { get; set; }
public List<VAST.Common.MediaType> MediaStreams { get; set; }
public Dictionary<EndPoint, VAST.Media.IMediaSink> ConnectedClients { get; set; }
}
Event Handlers
Connected
private void Server_Connected(object sender, VAST.Transport.TransportArgs e)
{
// new peer connected, it's unknown yet whether it's a client or publisher
}
Fired when a new TCP connection is established. At this point the peer's role (publisher or client) is not yet determined.
PublisherConnected
private void Server_PublisherConnected(object sender, VAST.Network.INetworkSource publisher)
{
publisher.Accept = true;
publisher.NewStream += Source_NewStream;
publisher.NewSample += Source_NewSample;
PublishedStream publishedStream = new PublishedStream(publisher.PublishingPath);
this.publishedStreams.Add(publisher.PublishingPath, publishedStream);
this.connectedPublishers.Add(publisher.EndPoint, publishedStream);
}
When a publisher connects via RTSP ANNOUNCE, the sample:
- Sets
Accept = trueto allow the connection - Subscribes to
NewStreamandNewSampleevents on the publisher - Creates a
PublishedStreamrecord and registers it by stream name and endpoint
ClientConnected
private void Server_ClientConnected(object sender, VAST.Network.INetworkSink client)
{
if (!this.publishedStreams.ContainsKey(client.PublishingPath))
{
return; // stream not published
}
PublishedStream publishedStream = this.publishedStreams[client.PublishingPath];
client.Accept = true;
int index = 0;
foreach (VAST.Common.MediaType mt in publishedStream.MediaStreams)
{
client.AddStream(index++, mt);
}
publishedStream.ConnectedClients.Add(client.EndPoint, client);
this.connectedClients.Add(client.EndPoint, publishedStream);
}
When a client connects via RTSP DESCRIBE/SETUP, the sample:
- Verifies the requested stream is currently published
- Sets
Accept = trueto allow the connection - Calls
AddStreamfor each media stream to inform the client of the available tracks - Adds the client to the published stream's connected clients dictionary
Disconnected
Handles both publisher and client disconnections:
- Publisher disconnect — stops all connected clients on that stream, removes the published stream
- Client disconnect — removes the client from the published stream's connected clients
Media Flow
NewStream Event
private void Source_NewStream(object sender, Media.NewStreamEventArgs e)
{
string publishingPath = (sender is VAST.Network.INetworkSource)
? ((VAST.Network.INetworkSource)sender).PublishingPath
: this.pullSourcePath;
PublishedStream publishedStream = this.publishedStreams[publishingPath];
publishedStream.MediaStreams[e.StreamIndex] = e.MediaType.Clone();
}
Fired when a source announces a new media stream. The handler determines the publishing path from the sender — either an INetworkSource (remote publisher) or a local source (pull/image). The media type is saved for clients that connect later.
NewSample Event
private void Source_NewSample(object sender, Media.NewSampleEventArgs e)
{
string publishingPath = (sender is VAST.Network.INetworkSource)
? ((VAST.Network.INetworkSource)sender).PublishingPath
: this.pullSourcePath;
PublishedStream publishedStream = this.publishedStreams[publishingPath];
foreach (VAST.Media.IMediaSink client in publishedStream.ConnectedClients.Values)
{
client.PushMedia(e.Sample.StreamIndex, e.Sample);
}
}
Each media sample is forwarded to all connected clients via PushMedia. The same handler works for both remote publishers and local sources.
Optional: Pull Source
The sample includes a commented-out example of pulling a stream from a remote RTSP server and re-serving it to clients:
this.pullSourcePath = "pull";
PublishedStream publishedStream = new PublishedStream(this.pullSourcePath);
this.publishedStreams.Add(this.pullSourcePath, publishedStream);
VAST.RTSP.RtspClientSource source = new VAST.RTSP.RtspClientSource();
source.Uri = "rtsp://192.168.0.101/stream";
source.NewStream += Source_NewStream;
source.NewSample += Source_NewSample;
source.StateChanged += Source_StateChanged;
source.Error += Source_Error;
source.Open();
The pull source opens asynchronously. When it enters the Opened state, Start() is called to begin receiving media:
private void Source_StateChanged(object sender, Media.MediaState e)
{
if (e == VAST.Media.MediaState.Opened && sender is VAST.RTSP.RtspClientSource)
{
((VAST.RTSP.RtspClientSource)sender).Start();
}
}
Because the pull source uses the same Source_NewStream and Source_NewSample handlers, its media samples are forwarded to connected clients automatically.
Optional: Image Source
The sample also includes a commented-out example of serving a static image as a live H.264 video stream:
this.pullSourcePath = "image";
PublishedStream publishedStream = new PublishedStream(this.pullSourcePath);
this.publishedStreams.Add(this.pullSourcePath, publishedStream);
VAST.Image.ImageSource source = new VAST.Image.ImageSource();
source.SetImage(@"C:\Media\logo.png");
source.NewStream += Source_NewStream;
source.NewSample += Source_NewSample;
source.Error += Source_Error;
source.Open();
VAST.Common.MediaType mt = new VAST.Common.MediaType
{
ContentType = VAST.Common.ContentType.Video,
CodecId = VAST.Common.Codec.H264,
Bitrate = 1000000,
Width = 1280,
Height = 720,
Framerate = new VAST.Common.Rational(30),
};
source.SetDesiredOutputType(0, mt);
source.Start();
The ImageSource encodes the image into a continuous H.264 stream. Like the pull source, it reuses the same Source_NewStream and Source_NewSample handlers for forwarding to clients.
Access URLs
| Protocol | URL |
|---|---|
| RTSP Publish | rtsp://server/{stream-name} |
| RTSP Play | rtsp://server/{stream-name} |
| RTSPS Publish | rtsps://server:322/{stream-name} |
| RTSPS Play | rtsps://server:322/{stream-name} |
See Also
- Sample Applications — overview of all demo projects
- .NET Server Demo — parent page with setup instructions, license key configuration, and access URL reference
- VAST.RTSP Library — RTSP/RTP API reference