Starting template for OvenMediaEngine
Go to file
Hamcha 1c27053a9d
check in
2024-06-21 18:40:58 -04:00
config check in 2024-06-21 18:40:58 -04:00
player check in 2024-06-21 18:40:58 -04:00 check in 2024-06-21 18:40:58 -04:00
docker-compose.yml check in 2024-06-21 18:40:58 -04:00
ome.html check in 2024-06-21 18:40:58 -04:00 check in 2024-06-21 18:40:58 -04:00


This is a template to get started using OvenMediaEngine. It has one very specific audience: Streamers proficient in server management and fucking around with HTML and code wanting to start selfhosting a bit more without quitting popular platforms like Twitch.

Most of this is my own setup which I've torn apart a little and added tons of comments everywhere I saw fit.

This README hopefully will work as a guide on how to use the provided files in this repo but feel free to hit me up at if you need some help.

What this is NOT: A comprehensive, beginner-friendly, battery-powered set-up. Prepare to get your hands dirty and possibly dig through some manuals. You will most definetely want to use this as a starting point only and customize the hell out of it.


The ingredients you will need to get started are:

  • Some server running any flavor of Docker that supports docker-compose
  • TLS certificate setup using LetsEncrypt or something else

You may be able to get away without a certificate setup but you'd be playing a risky game.

Setting up the oven

Clone this repo somewhere, look at the docker compose file.

Make sure the ports used by OME are available. If you need to change some, note them down since you'll have to change them in the config file as well.

Set the OME_HOST_IP to whatever IP or domain name your server is reachable at. You're gonna use that IP/domain for your RTMP ingest and output feeds.

The config folder contains the configuration files, including the most important one, Server.xml. I added plenty of comments to it so please look through it all and configure it to your liking.

Once everything has been set up properly, start it with docker compose up (and add -d for detached yadda yadda hopefully you know the deal)

The SignedPolicy bit

The config file specifies two credentials to set up, a API access key (for later) and something about SignedPolicy. SignedPolicy is the main authentication method that OME uses to prevent anyone from streaming your content. It has additional benefits like being able to provide lock content or transcoding options behing paywalls as well as provide self-expiring URLs for both streaming and viewing. You can explore the whole thing for yourself but for us it's mostly an annoyance, here is how it works:

Every URL for either viewing (through WebRTC or HLS) and streaming (RTMP, SRT, WHIP) must be accompanied by a policy object that provides limits to that URL (at minimum an expiration date) and a signature that signs the whole thing as "valid" by whoever has the secret key.

In other words, you'll need to use whatever secret key you specified in the config file to create signed URLs both to put in your OBS for streaming and in whatever web player for viewing.

OME provides a very bland shell script for this but I re-implemented all of it as a webpage on (all running with clientside javascript)

If you don't trust using a website you can use the ome.html page bundled in this repo, it's the same file and its code is not minimized in any way (feel free to snoop to see how the signing process works, in fact).

The URLs you're looking to sign are most likely the following:

Viewer endpoints (WebRTC, HLS)

These URLs are used in the web player for letting users access your stream, they look something like this:

  • wss://your.server.ip:3334/app/stream/webrtc
  • https://your.server.ip:3334/app/stream/llhls.m3u8

after using the tool to sign them, they would look something like this:


This is what you need to use as the URL in your web player.


To stream to OME you'll also need a signed URL. For RTMP you'll need to sign something akin to the following:


after using the tool, it should look like this:


To use it in OBS, you'll need to split it in two like this:

  • For server, use rtmp://your.server.ip:1935/app
  • For stream key, use stream?policy=eyJ1cmxfZXhwaXJlIjo2Mjg5MzE4ODAwMDAwfQ&signature=fjS6rae_bzOU6pP_SrM7aRI7eVs

SRT and WHIP are work in progress since they're a bit annoying to figure out, I got it once and forgot about it, whoops. I'll update this later I swear!

Setting up the player

The player is the most scuffed part of it all currently. I provided you with my scuffy setup for you to hack at, just mess with the HTML and JS file until it looks passable to you. The biggest problem is that there is no proper offline state and the player will error out when a stream isn't playing. I'd like to make it proper but for now it is what it is.


You will have noticed the config file has nothing about restreaming to Twitch/Youtube. This is a bit of a weird thing for me as well because currently the only way to restream via the RTMP publisher (which is enabled via the config) is to use API calls to set ip up and start it.

WIP! Gonna fill this up later, I forgot my Bruno workspace at home and need to finesse my current overlay setup so I can include it in here.


I currently use a LetsEncrypt setup for certificates, which mean they change roughly every couple months. This means OME needs to reload the cert files every once in a while and I do this with a cronjob that just copies those files over and restarts the container. If you have an acme.json file because of managing certificates through tools like Traefik, check on how to extract specific certificates/keys from that.

The API bit is annoying not only because it's not configurable via the config file but also because you have to redo the calls basically every stream. For some reasons the "pushes" expire in a fashion that I cannot seem to understand. I just use the overlay solution described. I hope a nicer solution comes up to address this.


I guess this needs some? The bits in player/vendor are provided under their own permissive license, treat anything else as WTFPL or whatever.