Skip to content

RTMP Server Events

There are multiple event handlers available, including IRtmpServerConnectionEventHandler, IRtmpServerStreamEventHandler, IRtmpMediaMessageInterceptor and IRtmpMediaCachingInterceptor, which are useful for extending the behavior of LiveStreamingServerNet.

Interfaces

Below are the interfaces for these event handlers:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
public interface IRtmpServerConnectionEventHandler
{
    int GetOrder() => 0;
    ValueTask OnRtmpClientCreatedAsync(IEventContext context, ISessionControl client);
    ValueTask OnRtmpClientDisposingAsync(IEventContext context, uint clientId);
    ValueTask OnRtmpClientDisposedAsync(IEventContext context, uint clientId);
    ValueTask OnRtmpClientHandshakeCompleteAsync(IEventContext context, uint clientId);
    ValueTask OnRtmpClientConnectedAsync(IEventContext context, uint clientId, IReadOnlyDictionary<string, object> commandObject, IReadOnlyDictionary<string, object>? arguments);
}

public interface IRtmpServerStreamEventHandler
{
    int GetOrder() => 0;
    ValueTask OnRtmpStreamPublishedAsync(IEventContext context, uint clientId, string streamPath, IReadOnlyDictionary<string, string> streamArguments);
    ValueTask OnRtmpStreamUnpublishedAsync(IEventContext context, uint clientId, string streamPath);
    ValueTask OnRtmpStreamSubscribedAsync(IEventContext context, uint clientId, string streamPath, IReadOnlyDictionary<string, string> streamArguments);
    ValueTask OnRtmpStreamUnsubscribedAsync(IEventContext context, uint clientId, string streamPath);
    ValueTask OnRtmpStreamMetaDataReceivedAsync(IEventContext context, uint clientId, string streamPath, IReadOnlyDictionary<string, object> metaData);
}

public interface IRtmpMediaMessageInterceptor
{
    ValueTask OnReceiveMediaMessageAsync(uint clientId, string streamPath, MediaType mediaType, IRentedBuffer rentedBuffer, uint timestamp, bool isSkippable);
}

public interface IRtmpMediaCachingInterceptor
{
    ValueTask OnCacheSequenceHeaderAsync(uint clientId, string streamPath, MediaType mediaType, byte[] sequenceHeader);
    ValueTask OnCachePictureAsync(uint clientId, string streamPath, MediaType mediaType, IRentedBuffer rentedBuffer, uint timestamp);
    ValueTask OnClearGroupOfPicturesCacheAsync(uint clientId, string streamPath);
}

Usage Example

For instance, if you want to limit the publishing time of every stream to a maximum of 30 minutes, you can do the following:

Implement IRtmpServerStreamEventHandler

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
using LiveStreamingServerNet.Networking.Server.Contracts;
using LiveStreamingServerNet.Rtmp.Server.Contracts;
using LiveStreamingServerNet.Utilities.Contracts;
using Microsoft.Extensions.Options;
using System.Collections.Concurrent;

public class PublishingTimeLimiterConfig
{
    public int PublishingTimeLimitSeconds { get; set; }
}

public class PublishingTimeLimiter : IRtmpServerStreamEventHandler, IDisposable
{
    private readonly ConcurrentDictionary<uint, ITimer> _clientTimers = new();
    private readonly IServer _server;
    private readonly PublishingTimeLimiterConfig _config;

    public PublishingTimeLimiter(IServer server, IOptions<PublishingTimeLimiterConfig> config)
    {
        _server = server;
        _config = config.Value;
    }

    public void Dispose()
    {
        foreach (var timer in _clientTimers.Values)
            timer.Dispose();

        _clientTimers.Clear();
    }

    public ValueTask OnRtmpStreamPublishedAsync(
        IEventContext context, uint clientId, string streamPath, IReadOnlyDictionary<string, string> streamArguments)
    {
        _clientTimers[clientId] = new Timer(async _ =>
        {
            var client = _server.GetClient(clientId);

            if (client != null)
                await client.DisconnectAsync();
        }, null, TimeSpan.FromSeconds(_config.PublishingTimeLimitSeconds), Timeout.InfiniteTimeSpan);

        return ValueTask.CompletedTask;
    }

    public ValueTask OnRtmpStreamUnpublishedAsync(IEventContext context, uint clientId, string streamPath)
    {
        if (_clientTimers.TryRemove(clientId, out var timer))
            timer.Dispose();

        return ValueTask.CompletedTask;
    }

    public ValueTask OnRtmpStreamMetaDataReceivedAsync(
        IEventContext context, uint clientId, string streamPath, IReadOnlyDictionary<string, object> metaData)
        => ValueTask.CompletedTask;

    public ValueTask OnRtmpStreamSubscribedAsync(
        IEventContext context, uint clientId, string streamPath, IReadOnlyDictionary<string, string> streamArguments)
        => ValueTask.CompletedTask;

    public ValueTask OnRtmpStreamUnsubscribedAsync(IEventContext context, uint clientId, string streamPath)
        => ValueTask.CompletedTask;
}

The PublishingTimeLimiter, which implements IRtmpServerStreamEventHandler, will create a timer to disconnect the client after PublishingTimeLimitSeconds, and dispose the corresponding timer when the stream is unpublished.

Register the Event Handler

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
using LiveStreamingServerNet;
using LiveStreamingServerNet.Networking;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Logging;
using System.Net;

using var liveStreamingServer = LiveStreamingServerBuilder.Create()
    .ConfigureRtmpServer(options =>
    {
        options.Services.Configure<PublishingTimeLimiterConfig>(config =>
            config.PublishingTimeLimitSeconds = 60 * 30
        );

        options.AddStreamEventHandler<PublishingTimeLimiter>();
    })
    .ConfigureLogging(options => options.AddConsole())
    .Build();

await liveStreamingServer.RunAsync(new ServerEndPoint(new IPEndPoint(IPAddress.Any, 1935), false));

This code adds the implementation of IRtmpServerStreamEventHandler to the RTMP server.