Automatically create new channels

This tutorial describes how to build an engine implementation that dynamically create new channels that does not exist. One use case is to have a unique and fully personalized channel for each viewer.


We will first start of with a standard Channel Manager adapter implementation:

class MyChannelManager implements IChannelManager {
  private channels: Channel[] = [];

  constructor() {
    this.channels.push({ id: "1", profile: this._getProfile() });
  }

  getChannels(): Channel[] {
¨    return this.channels;
  }

  _getProfile(): ChannelProfile[] {
    return [
      { bw: 6134000, codecs: "avc1.4d001f,mp4a.40.2", resolution: [1024, 458] },
      { bw: 2323000, codecs: "avc1.4d001f,mp4a.40.2", resolution: [640, 286] },
      { bw: 1313000, codecs: "avc1.4d001f,mp4a.40.2", resolution: [480, 214] },
    ];
  }
}

A channel manager that has one channel with Id 1 initially and available at the playback URL /channels/1/master.m3u8. What we now want to achieve is that if a player would use the playback URL /channels/foo/master.m3u8 it should create a new channel with Id foo. To achieve this we will extend the channel manager with a function autoCreateChannel that is called by the engine when a new channel is requested. That is, a channel that the engine has not found in the current list of channels from the channel manager.

  autoCreateChannel(channelId: string) {
    if (!this.channels.find((ch) => ch.id === channelId)) {      
      this.channels.push({ id: channelId, profile: this._getProfile() });
    }
  }

This implementation above will just create a new channel with a default profile if is not already in the list. Note that this implementation does not persist anything and on a restart of the server the channel list will be back to default. The channel manager implementation will now look like this.

class MyChannelManager implements IChannelManager {
  private channels: Channel[] = [];

  constructor() {
    this.channels.push({ id: "1", profile: this._getProfile() });
  }

  getChannels(): Channel[] {
¨    return this.channels;
  }

  autoCreateChannel(channelId: string) {
    if (!this.channels.find((ch) => ch.id === channelId)) {      
      this.channels.push({ id: channelId, profile: this._getProfile() });
    }
  }

  _getProfile(): ChannelProfile[] {
    return [
      { bw: 6134000, codecs: "avc1.4d001f,mp4a.40.2", resolution: [1024, 458] },
      { bw: 2323000, codecs: "avc1.4d001f,mp4a.40.2", resolution: [640, 286] },
      { bw: 1313000, codecs: "avc1.4d001f,mp4a.40.2", resolution: [480, 214] },
    ];
  }
}

However, to enable this feature you also need to set the engine option autoCreateSession to true, for example:

const engineOptions: ChannelEngineOpts = {
  heartbeat: "/",
  averageSegmentDuration: 2000,
  channelManager: myChannelManager,
  autoCreateSession: true,
  defaultSlateUri:
    "https://maitv-vod.lab.eyevinn.technology/slate-consuo.mp4/master.m3u8",
  slateRepetitions: 10,
};

const engine = new ChannelEngine(myAssetManager, engineOptions);
engine.start();
engine.listen(process.env.PORT || 8000);

In the asset manager you will get the channel Id in the Vod Request payload as usual (VodRequest.playlistId) and from there you place the logic what VOD to be served to the engine in this case. If you are creating a channel based on each viewer you would probably ask a recommendation engine for that particular viewer on what to play next.