Service: FAST Channel Engine - EyevinnOSC/community GitHub Wiki

Getting started

This tutorial walks you through how to setup linear TV channels for streaming in Open Source Cloud. Based on manifest-manipulation technology and reusing the already transcoded on-demand files no live encoding or transcoding is necessary. This enables you to create linear TV channels where the quality of the on-demand files are maintained.

Providing this service is the open source project Channel Engine and it is available as a service in Open Source Cloud.

What is FAST Channel Engine?

The FAST Channel Engine is a sustainable system for generating linear streaming channels from VOD content. Based on VOD2Live technology and the Eyevinn Channel Engine library, it enables organizations to create numerous FAST channels with significantly lower energy consumption compared to traditional live-transcoded FAST channels.

The system employs a flexible plugin architecture that enables various content sourcing mechanisms, ranging from simple looping of a single video to complex, scheduled programming with dynamic ad insertion capabilities.

Key Benefits of the FAST Channel Engine

  • Energy-efficient VOD2Live technology that eliminates redundant transcoding and packaging, consuming a fraction of the energy compared to traditional live-transcoded FAST channels.
  • Multiple plugin options for different channel creation needs
  • Ad insertion capabilities with preroll support across all plugins
  • Passthrough of ad opportunity markers and proven integration with Server-Side Ad Insertion providers

The FAST Channel Engine is built on the open-source Eyevinn Channel Engine library and uses VOD2Live technology to create live-like channels from VOD content. The key differentiator from traditional FAST solutions is the elimination of live transcoding, which significantly reduces resource usage and energy consumption.

The system is designed to be highly configurable through environment variables and supports various deployment options from simple single-channel setups to complex multi-channel deployments with scheduling capabilities.

Prerequisites

  • If you have not already done so, sign up for an OSC account.
  • Video files transcoded to VOD files. This documentation describes how you can create these VOD files with Open Source Cloud services.

Limitations

  • Only VOD files of HLS streaming format is supported. If you need to output a channel in both HLS and MPEG-DASH we recommend using a Channel Engine Bridge to a repackager origin.
  • The FAST Channel Engine service does not currently support integration with the FAST Engine Scheduling service.

Linear channel from looping a VOD

The most basic example is to create a linear TV channel by looping a single VOD. In this example we will create a channel by looping the Channel Engine promotion video here:

Step 1: Create channel

Navigate to the FAST Channel Engine service in OSC web user interface.

Click on button "Create channel +".

Enter the following values in the channel creation dialog.

  • Name: guide
  • Type: Loop
  • URL: https://lab.cdn.eyevinn.technology/Channel-Engine-Promo-Mar-2023-PnA8E-jw5x.mp4/manifest.m3u8
  • Demuxed audio: false
  • VTT Subtitles: false

Our VOD does not have demuxed audio (audio separated from video segments) and no VTT subtitles.

Skärmavbild 2024-07-12 kl  14 21 44

Now press Create.

Skärmavbild 2024-07-12 kl  14 23 23

You can also create a channel using the OSC command line tool. Run the following command to create the channel.

% osc create channel-engine guide \
  -o type=Loop \
  -o url=https://lab.cdn.eyevinn.technology/Channel-Engine-Promo-Mar-2023-PnA8E-jw5x.mp4/manifest.m3u8 \
  -o opts.useDemuxedAudio=false \
  -o opts.useVttSubtitles=false

Instance created:
{
  id: 'demo-guide',
  name: 'guide',
  type: 'Loop',
  url: 'https://lab.cdn.eyevinn.technology/Channel-Engine-Promo-Mar-2023-PnA8E-jw5x.mp4/manifest.m3u8',
  opts: { useDemuxedAudio: false, useVttSubtitles: false },
  playback: 'https://demo.ce.prod.osaas.io/channels/guide/master.m3u8'
}

Step 2: Watch channel

Provide the HLS capable video player with the HLS playback URL that you received and available in the instance card.

Skärmavbild 2024-07-12 kl  14 25 53

Linear channel from looping a playlist of VODs

In this example we will create a channel from looping a playlist of the following VOD files:

https://lab.cdn.eyevinn.technology/stswetvplus-promo-2023-5GBm231Mkz.mov/manifest.m3u8
https://lab.cdn.eyevinn.technology/Channel-Engine-Promo-Mar-2023-PnA8E-jw5x.mp4/manifest.m3u8
https://lab.cdn.eyevinn.technology/eyevinn-reel-feb-2023-_2Y7i4eOAi.mp4/manifest.m3u8

Step 1: Create playlist

The playlist is a text file with a list of VOD URLs separated with newlines. You provide the engine with a URL to this playlist so it needs to be public accessible. There are many ways but for the purpose of this guide we will be using a Gist on GitHub.

  1. Go to https://gist.github.com
  2. Enter a name of the playlist in Filename (e.g. playlist.txt)
  3. Enter a list of URL to a VOD (one per line)
  4. Press Create public gist (green button)
  5. Press "Raw" on your created playlist file
  6. Copy the URL to the created playlist file e.g. https://gist.githubusercontent.com/birme/bc8854436a2b3cdbc891efdaa960b528/raw/52fcdfba0cf05ca8c13185b27e6767d6ebdd8a7f/gistfile1.txt

Step 2: Create channel

Navigate to the FAST Channel Engine service in OSC web user interface.

Click on button "Create channel +".

Enter the following values in the channel creation dialog.

  • Name: guide
  • Type: Playlist
  • URL: https://gist.githubusercontent.com/birme/bc8854436a2b3cdbc891efdaa960b528/raw/52fcdfba0cf05ca8c13185b27e6767d6ebdd8a7f/gistfile1.txt
  • Demuxed audio: false
  • VTT Subtitles: false

Our VOD files does not have demuxed audio (audio separated from video segments) and no VTT subtitles.

Skärmavbild 2024-07-12 kl  14 46 47

Now press Create.

You can also create a channel using the OSC command line tool. Run the following command to create the channel.

% osc create channel-engine guide \
  -o type=Playlist \
  -o url=https://gist.githubusercontent.com/birme/bc8854436a2b3cdbc891efdaa960b528/raw/52fcdfba0cf05ca8c13185b27e6767d6ebdd8a7f/gistfile1.txt \
  -o opts.useDemuxedAudio=false \
  -o opts.useVttSubtitles=false

Instance created:
{
  id: 'demo-guide',
  name: 'guide',
  type: 'Playlist',
  url: 'https://gist.githubusercontent.com/birme/bc8854436a2b3cdbc891efdaa960b528/raw/52fcdfba0cf05ca8c13185b27e6767d6ebdd8a7f/gistfile1.txt',
  opts: { useDemuxedAudio: false, useVttSubtitles: false },
  playback: 'https://demo.ce.prod.osaas.io/channels/guide/master.m3u8'
}

Step 2: Watch channel

Provide the HLS capable video player with the HLS playback URL that you received and available in the instance card.

Linear channel using a WebHook

In this example we will configure the engine to call a webhook that responds with a VOD URL of what to play next. This enables a more dynamic channel where decision of what to play can be taken just in time. We will use the Web Runner service that enables you to run custom code in Eyevinn Open Source Cloud.

Step 1: Building the webhook

We will develop a basic webhook in Javascript that picks a random VOD from a list for VODs and inserts a pre-roll house ad in the beginning. Create a folder called mywebhook and initiate a Node javascript project.

% mkdir webhook
% cd webhook/
% npm init

Initiate the project with the default values. In this example we will use fastify for the API middleware so will need to install that dependency.

% npm install --save fastify

Create file called index.js in your project that contains this code.

const fastify = require('fastify');
const { randomUUID } = require('node:crypto');

const app = fastify();
app.get('/nextVod', (request, reply) => {
  const channelId = request.query.channelId;
  console.log(`Requesting next VOD for channel ${channelId}`);

  const vods = [
    'https://lab.cdn.eyevinn.technology/stswetvplus-promo-2023-5GBm231Mkz.mov/manifest.m3u8',
    'https://lab.cdn.eyevinn.technology/Channel-Engine-Promo-Mar-2023-PnA8E-jw5x.mp4/manifest.m3u8',
    'https://lab.cdn.eyevinn.technology/eyevinn-reel-feb-2023-_2Y7i4eOAi.mp4/manifest.m3u8'
  ];
  const vodResponse = {
    id: randomUUID(),
    title: 'Example',
    hlsUrl: vods[Math.floor(Math.random() * vods.length)],
    prerollUrl: 'https://maitv-vod.lab.eyevinn.technology/VINN.mp4/master.m3u8',
    prerollDurationMs: 105000
  };
  reply.send(vodResponse);
});

app.listen({ port: 8080, host: '0.0.0.0' }, (err, address) => {
  if (err) console.error(err);
  console.log(`Server listening at ${address}`);
});

Add the following to the package.json file:

{
  ...
  "scripts": {
    "start": "node index.js"
  },
  ...
}

Then start the server:

% npm start
> [email protected] start
> node index.js

Server listening at http://127.0.0.1:8080

You can try it out using curl:

% curl "http://localhost:8080/nextVod?channelId=foo"
{"id":"96811e90-346f-459e-b540-38846617ca97","title":"Example","hlsUrl":"https://lab.cdn.eyevinn.technology/Channel-Engine-Promo-Mar-2023-PnA8E-jw5x.mp4/manifest.m3u8","prerollUrl":"https://maitv-vod.lab.eyevinn.technology/VINN.mp4/master.m3u8","prerollDurationMs":105000}

Step 2: Deploy the webhook as Web Runner

Initialize an empty Git repository and commit your files.

% git init
% git add index.js package-lock.json package.json
% git commit -m "my webhook"

Sign in to GitHub and create a repository where you push this code to. Then create a Web Runner for this repository by following the Web Runner instructions.

Skärmavbild 2025-05-15 kl  21 29 44

  • Name: mywebhook
  • GitHubUrl: <link-to-your-github-repository>
  • GitHubToken: <your-github-token>

The URL to the webhook is found on the instance card, for example https://eyevinnlab-mywebhook.eyevinn-web-runner.auto.prod.osaas.io. Verify that the webhook is up and running with curl.

% curl "https://eyevinnlab-mywebhook.eyevinn-web-runner.auto.prod.osaas.io/nextVod?channelId=foo"
{"id":"e08d4ff6-b125-4795-80df-de037896bbbe","title":"Example","hlsUrl":"https://lab.cdn.eyevinn.technology/Channel-Engine-Promo-Mar-2023-PnA8E-jw5x.mp4/manifest.m3u8","prerollUrl":"https://maitv-vod.lab.eyevinn.technology/VINN.mp4/master.m3u8","prerollDurationMs":105000}

Now you have a webhook available that can provide the engine on what to play next.

Step 3: Create channel

When we now create the channel we choose the plugin WebHook and the URL to the endpoint provided by the Web Runner above, which in this case is https://eyevinnlab-mywebhook.eyevinn-web-runner.auto.prod.osaas.io/nextVod

Skärmavbild 2025-05-15 kl  21 42 22

Step 3: Watch

Provide the HLS capable video player with the HLS playback URL that you received and available in the instance card.

Insertion of mid-roll ad opportunities

We are now going to describe how to insert ad opportunities in the stream to enable monetization with in-stream ads. The way this works is that we will insert a "house ad" in the source VOD that is played out by the engine. This house ad gives an opportunity to be replaced using a server-side ad inserter (Yospace, AWS MediaTailor, Nowtilus, to mention a few). The server-side ad inserter is connected to the ad server who takes decision on what ads to serve for a particular user or session.

1*H0zjpw60Ya31dEElKBwoaA

The diagram above illustrates how a setup can look and in this case using the open source Test Adserver to emulate a real ad server.

To insert house ad in the source VOD we can use the HLS VOD Stitcher available in Open Source Cloud.

Step 1: Create stitcher

Launch an HLS VOD Stitcher instance.

This stitcher instance is a proxy between the original source VOD and the engine and will insert another VOD at the instructed position. We instruct the proxy the location of the source VOD, location of the VOD to insert and at what position to insert it by constructing a payload that is passed to the proxy as a query parameter.

Step 2: Generate payload for stitcher

The payload is a base64 encoded JSON and can look like this.

{
  "uri": "https://maitv-vod.lab.eyevinn.technology/UNHINGED_Trailer_2020.mp4/master.m3u8",
  "breaks": [
    { "pos": 20000, "duration": 15000, "url": "https://maitv-vod.lab.eyevinn.technology/VINN.mp4/master.m3u8" }
  ]
}

When you base64 encode the above JSON you get:

ewogICJ1cmkiOiAiaHR0cHM6Ly9tYWl0di12b2QubGFiLmV5ZXZpbm4udGVjaG5vbG9neS9VTkhJTkdFRF9UcmFpbGVyXzIwMjAubXA0L21hc3Rlci5tM3U4IiwKICAiYnJlYWtzIjogWwogICAgeyAicG9zIjogMjAwMDAsICJkdXJhdGlvbiI6IDE1MDAwLCAidXJsIjogImh0dHBzOi8vbWFpdHYtdm9kLmxhYi5leWV2aW5uLnRlY2hub2xvZ3kvVklOTi5tcDQvbWFzdGVyLm0zdTgiIH0KICBdCn0=

Step 3: Test the payload

We can test this payload by providing it to the stitcher we created. Open up the following URL in an HLS video player:

https://eyevinn-guide.eyevinn-lambda-stitch.auto.prod.osaas.io/stitch/master.m3u8?payload=ewogICJ1cmkiOiAiaHR0cHM6Ly9tYWl0di12b2QubGFiLmV5ZXZpbm4udGVjaG5vbG9neS9VTkhJTkdFRF9UcmFpbGVyXzIwMjAubXA0L21hc3Rlci5tM3U4IiwKICAiYnJlYWtzIjogWwogICAgeyAicG9zIjogMjAwMDAsICJkdXJhdGlvbiI6IDE1MDAwLCAidXJsIjogImh0dHBzOi8vbWFpdHYtdm9kLmxhYi5leWV2aW5uLnRlY2hub2xvZ3kvVklOTi5tcDQvbWFzdGVyLm0zdTgiIH0KICBdCn0=

When playing this you will see that around 20 seconds in the VOD you see the house ad.

Step 4: Use the stitched VOD as source

As an example we will create channel where we use the stitched VOD as the source VOD. Create a playlist containing the URL above:

https://eyevinn-guide.eyevinn-lambda-stitch.auto.prod.osaas.io/stitch/master.m3u8?payload=ewogICJ1cmkiOiAiaHR0cHM6Ly9tYWl0di12b2QubGFiLmV5ZXZpbm4udGVjaG5vbG9neS9VTkhJTkdFRF9UcmFpbGVyXzIwMjAubXA0L21hc3Rlci5tM3U4IiwKICAiYnJlYWtzIjogWwogICAgeyAicG9zIjogMjAwMDAsICJkdXJhdGlvbiI6IDE1MDAwLCAidXJsIjogImh0dHBzOi8vbWFpdHYtdm9kLmxhYi5leWV2aW5uLnRlY2hub2xvZ3kvVklOTi5tcDQvbWFzdGVyLm0zdTgiIH0KICBdCn0=

Provide this playlist when creating the channel.

If using a WebHook you would provide the stitch VOD as the next VOD to play in the response. For example given this webhook:

const { randomUUID } = require('crypto');

exports.handler = async (event) => {
  const channelId = event.query.channelId;
  console.log(`Requesting next VOD for channel ${channelId}`);

  const vods = [
    'https://lab.cdn.eyevinn.technology/stswetvplus-promo-2023-5GBm231Mkz.mov/manifest.m3u8',
    'https://lab.cdn.eyevinn.technology/Channel-Engine-Promo-Mar-2023-PnA8E-jw5x.mp4/manifest.m3u8',
    'https://lab.cdn.eyevinn.technology/eyevinn-reel-feb-2023-_2Y7i4eOAi.mp4/manifest.m3u8'
  ];
  const vod = vods[Math.floor(Math.random() * vods.length)];
  const payload = {
    "uri": vod,
    "breaks": [
      { "pos": 20000, "duration": 15000, "url": "https://maitv-vod.lab.eyevinn.technology/VINN.mp4/master.m3u8" }
    ]
  };
  const encodedPayload = Buffer.from(JSON.stringify(payload)).toString('base64');
  return {
    body: {
      id: randomUUID(),
      title: 'Example',
      hlsUrl: 'https://eyevinn-guide.eyevinn-lambda-stitch.auto.prod.osaas.io/stitch/master.m3u8?payload=' + encodedPayload
    }
  };
};

Step 5: Watch channel

Now when playing the channel we created you will find that 20 seconds into the VOD there will be house ad inserted. When this channel is passed to an server-side ad inserter this house ad will be replaced with ad decided by the ad server. This is outside the scope of this guide.

Additional Documentation

This guide explains how to set up a Channel Engine Bridge in Open Source Cloud to push a FAST Channel to an alternate live origin, such as AWS MediaPackage or an AWS S3 bucket. It is intended for use cases where pulling directly from the channel engine is not suitable.