We (friends and I) have made our own little streaming platform for streaming games and stuff based on the RTMP module for nginx. But since RTMP - being intertwined with Flash - is a rather limited protocol that only supports FLV (and thus H264 + ACC or MP3) we’ve been looking for alternatives. After searching the intertubes we found out that there are no good, simple streaming servers that would be suitable for a small group of people and allow all sorts of media to be streamed. Things like H265 video, Opus audio, subtitle tracks, etc. were on our nice-to-have list, but no streaming application at the time supported them. But the matroska container format does.

As it turns out, Matroska files are actually live-streamable with just a few header tweaks and a custom HTTP server. So I started reading the specs and built a straight-forward Matroska IO library in C#. I then used this library to build a simple HTTP server that would allow any Matroska file to be POSTed to it while simultaneously being downloaded by multiple clients. At first, I used my own hacked-together HTTP implementation in order to not have to deal with Mono and its incomplete and/or buggy ASP.net implementation on GNU/Linux. However, after .NET Core came around, I switched to ASP.NET Core. (ASP).NET Core is officially supported on a wide range of platforms, including Linux-based systems.

The result of this undertaking is a rather uncomplicated C# app. Any matroska stream (.mkv, .mka) can be posted to any location on the server. The app caches and modifies the relevant header information of the stream so a client can connect to an already running stream. Once a GET request is sent for the same location that is already being POSTed, the headers are sent and the server waits for the next keyframe in the source stream. Once that happens, the data from the “streamer” is simply being relayed to the “viewer”, resulting in an almost real-time multi-media stream.

The server itself has no concept of users or channels. However, simple access control can be achieved with scriptable hooks. These are executed whenever a new stream is started and can allow or deny the request. This makes it possible to limit the possible locations (e.g. only a specific set of user names) and even implement password authentication through query strings (e.g. http://example.com/my-cool-stream.mkv?pw=hunter2) for streamers and viewers.

Building

To build the server, you’ll need the dotnet SDK. After you’ve downloaded the source code, simply run dotnet build in the same directory as MatroskaServer.csproj.

The GitLab project/source code for server can be found here.

Configuring

There are two files that configure the server.

First, there’s hosting.json which configures Kestrel. For more details see here, but basically all you’d probably need is the URL at which the server will listen for requests:

{
	"server.urls": "http://0.0.0.0:8080;http://[::]:8080"
}

This will listen on port 8080 for both ipv4 and ipv6.

Then there’s settings.json which configures the matroska server.

{
	"OnPostBegin":
	{
		"FileName": "/path/to/script.sh",
		"Arguments": "post_begin"
	},
	"OnPostEnd":
	{
		"FileName": "/path/to/script.sh",
		"Arguments": "post_end"
	},
	"OnGetBegin":
	{
		"FileName": "/path/to/script.sh",
		"Arguments": "get_begin"
	},
	"OnGetEnd":
	{
		"FileName": "/path/to/script.sh",
		"Arguments": "get_end"
	}
}

This configures the script or program to launch for the specific types of actions (POST begin/end and GET begin/end). All hooks are optional. You can put anything you want into "Arguments". The script will be launched with two environment variables: NAME, which contains the location (e.g. “foo” for http://example.com/foo?pw=bar) and PASSWORD, which contains the value of the pw query string variable (e.g. “bar” for that url). If you need any more parameters, feel free to add them in ChannelController.cs. The return value of that script is used to determine whether the server accepts or denies the request. If the script returns 0, the request may proceed. Otherwise, the server responds with “Forbidden”.

Running

Simply do dotnet run in the folder that contains MatroskaServer.csproj. When the server is running, you can get some info about live streams and viewers by opening /status.json in your browser.


To stream something you can use FFmpeg like so:

ffmpeg -re -i somefile -f matroska -live 1 "http://example.com/my-cool-stream.mkv?pw=hunter2"

OBS Studio also works, albeit with a little trickery. You need to configure it to record your stream using FFmpeg, then configure FFmpeg to stream to the server like this: OBS screenshot You’re free to configure the encoder(s) as you see fit of course. The only really required settings are “FFmpeg Output Type”, “File path or URL” and “Container Format”. The rest is up to you (and your internet connection). One downside to this is that OBS Studio can’t show you whether you’re dropping frames and also can’t really recover from a lag spike, should one occur. It’ll simply keep buffering, probably until you’re out of RAM. Maybe the OBS devs will one day implement native support for other protocols than RTMP.


Viewing a stream should be possible with any media player that supports MKV and real-time playback of HTTP files. I’ve tested this with mpv and VLC. Firefox doesn’t appear to work although it should theoretically support matroska because of webm, which is just a dumbed down version of matroska.

For mpv, all you need to do is open the URL:

mpv "http://example.com/my-cool-stream.mkv"

For VLC, go to Media -> Open Network Stream (or press Ctrl+N) and paste your stream URL there.