Table of Contents

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:

  1. Creates an RtspServer with configurable parameters
  2. Subscribes to connection events and manages publisher/client dictionaries
  3. Forwards media samples from publishers to all connected clients on the same stream
  4. 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:

  1. Sets Accept = true to allow the connection
  2. Subscribes to NewStream and NewSample events on the publisher
  3. Creates a PublishedStream record 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:

  1. Verifies the requested stream is currently published
  2. Sets Accept = true to allow the connection
  3. Calls AddStream for each media stream to inform the client of the available tracks
  4. 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