CDN Configuration with AWS Cloudfront

How to configure Cloudfront as a CDN to work with broadpeak.io

In Using broadpeak.io with a CDN we explain the reasons why you need to use a CDN in front of your broadpeak.io services in order to deliver your manipulated streams in the most optimal and efficient way.

In this playbook, we'll look at a couple of examples and illustrate how to do all this with the AWS Cloudfront product.

Prerequisites

You need to be familiar with how to configure a broadpeak.io service first. You'll find other playbooks in the "Getting Started" section of our documentation, as well as in the present "Playbooks" section.

For expediency, the examples in this playbook will use the same sources as those Getting Started articles.

Rules

The primary aspect of configuration for the CDN relates to routing. In an ABR stream, there are one or multiple manifests or playlists, and multiple media segments that the player will request in order to render the stream. The player connects to a single CDN, which needs to know where to send those requests. So it needs to be given a set of rules to follow. Those rules will be somewhat dependent on the type of service you are implementing.

There are some common rules that apply to all the types of services, and there are some rules that depend on the type of service, and the configuration of your sources.

The following table provides a summary of the rules that apply to your service type. The detail of those rules follow in the remainder of this document

Content ReplacementDynamic Ad InsertionVirtual Channel
Common Rules (source content)yesyesyes
Rules for Alternative Contentyesif using pre-packaged adsyes
Rules for Ad Segmentsnoif using broadpeak.io for transcoding of adsif using the Ad Insertion feature and using broadpeak.io for transcoding of ads
Rules for HLS subtitlesif using subtitlesif using subtitlesif using subtitles
Rules for Server-Side Ad Trackingnoif using SSATif using the Ad Insertion feature and SSAT

Common rules

For all types of services, there is a common set of rules that apply:

  1. Rules applying to the manifest requests, instructing the CDN to send those to broadpeak.io
  2. Rules applying to media segment requests, instructing the CDN to send those to the origin (ie. the server that stores the original packaged content)

Let's start with those.

We'll use https://origin.broadpeak.io/bpk-tv/bpkiofficial/hlsv3/index.m3u8 (one of our default samples) as the source, from which - in this case - a Content Replacement service has been created, the URL of which could be https://stream.broadpeak.io/a3eb043e7bf775ded159a8940be5e720/bpk-tv/bpkiofficial/hlsv3/index.m3u8.

Create Distribution

To start with, let's get terminology out of the way. In AWS parlance, we are setting up a Distribution. It defines a set or Origins, and Behaviors that define how requests coming to the distribution are routed to those Origins.

So, that's what we'll do.

🚧

Minimum functional configuration

There are dozens of settings that can be defined with Cloudfront. It's not the role of this playbook to explain them all. We'll concentrate on the minimum set to successfully define a production-ready distribution.

Some of the settings may also need adjusting based on your Origin servers and precise needs. If something isn't behaving as expected, don't hesitate to contact our Support staff at [email protected].

First, head to the AWS Console and click the Create Distribution button.

  1. The first step is to define the default Origin for the distribution. That is: the one that an incoming request will be sent to, in the absence of any behavior being triggered to redirect it elsewhere. So, in our case, that's the server that contains the original content (and in particular the media segments): origin.broadpeak.io. In this case the origin uses the HTTPS protocol, so that's what we'll set the origin to use as well.
  1. Next we define the default rule that defines how the CDN routes to that origin. Again, we probably want to restrict traffic to using the secure HTTPS protocol, and for streaming, only the GET and HEAD HTTP methods are necessary.
  1. One of the central roles of a CDN is to cache content in its network. So in defining that default behaviour, we also need to define whether the CDN is allowed to cache content. Since the default origin will be used to retrieve media segments, the answer is "absolutely". Cloudfront provides pre-configured caching policies that are sufficient for our need. We'll select the CachingOptimized policy.
  2. The origin request policy that appears underneath the cache policy defines what information is sent (through HTTP headers) from the CDN to the Origin. Unless your content origin has specific needs, you should be fine leaving it unspecified.
  3. Since the stream will usually be used in browser-based playback solutions, which enforce security rules when it comes to allowing a web page to retrieve resources (in this case the stream itself) from different origins than the page itself (those are called cross-origin resource sharing rules - or CORS), we need to have the CDN provide the right information to the requesting browser. There again, some pre-defined defaults are usable out of the box. Select CORS-With-Preflight.
  1. Finally, in the rest of that (long) screen, you should also:
    1. Disable security protections (unless you think this may be necessary for your situation)
    2. Define whether content should be distributed all over the world or in specific regions
  2. Right at the bottom, in the Description field, give it a nice and clear name that will allow you to retrieve it among your other distributions. I choose "broadpeak.io distribution"...

Having done all that, hit Create distribution at the bottom of the page. Assuming you've not forgotten anything essential, the next screen should show you a summary, and tell you (on the right) that your distribution is being deployed (under the Last modified heading)

That summary screen also shows you the domain name generated for your distribution, which will be in the form "uniqueid.cloudfront.net".

Add behavior(s) for manifests

So far, we've only really defined one origin and a default behaviour. We now need to define how the distribution needs to redirect calls for manifests and send them to broadpeak.io.

To do so, we need to first define a second Origin.

Click the Origins tab in the summary screen for your distribution, choose Create origin, and make it stream.broadpeak.io, over the HTTPS protocol.

Next, choose the Behaviors tab, and click Create behavior. This is when things start becoming more interesting.

Cloudfront, on receiving an incoming request on that distribution, will primarily determine where to send it based on matching a pattern in the URL path, ie. on searching the URL path (not taking into account any of the query parameters) for the presence of a particular sequence of characters.

So, here, since we want all requests for manifests to be sent to the new stream.broadpeak.io origin, we will use the standard extension. For HLS, it's *.m3u8, in which the * wildcard allows any sequence of characters before the .m3u8 sequence.

As earlier, we need to set caching and CORS policies as well. For the latter, you can use the same CORS-With-Preflight. But you do not want any caching, since we need each individual stream to be personalized through broadpeak.io. So, choose CachingDisabled in this case.

The broadpeak.io expects certain incoming headers (which allow personalization) being passed by the CDN. There is once again a predefined policy that works quite well in most cases: Elemental-MediaTailor-PersonalizedManifests

If you use DASH instead of HLS, then it's all the same as above, but use *.mpd as path pattern instead. And if you want both DASH and HLS, you need 2 separate behaviors.

After each change to the distribution configuration, AWS will perform a redeployment. The configuration will only be active when that summary screen (under the General tab) shows a date and time under Last modified. Just a few minutes...

... and then you can already start testing your setup. Although there are still plenty of rules missing, the basics are in place and you should be able to preview your service through the distribution. That's easily done in the broadpeak.io webapp, in which you can paste your distribution domain name.

Replacement Content

So far so good, but all we've done at this stage is fundamentally tackle the routing for the original source. When you configure a broadpeak.io service, you'll want to involve other content too. In particular, when working with Content Replacement and/or Virtual Channel services, you will normally want to stitch in content (specifically media segments) that comes from other places, usually different origins.

If that content comes from the same origin (as is the case in our Getting Started examples for Virtual Channel and Content Replacement services), then there's nothing left to do. Your service should be operational.

Something though, your content may come from different domains. Extending your distribution configuration to cover those is an extension of the work done so far:

  • add new Origins for those domains
  • and then Behaviors to route requests for the substituted media segments towards those

You'll do that based on the (partial) path to that content. So you need to be able to define a path pattern that uniquely identifies content coming from those alternate origins.

For example, let's imagine that we have VOD assets that sit on an alternate origin, which we want stitched in the stream. Here is one: https://bpkiosamples.s3-eu-west-1.amazonaws.com/AVOD/TOS-original-24fps-1080p/conditioned/stream.m3u8.

By adding it to a slot in the service, the stream output might look like the following:

#EXTM3U
#EXT-X-VERSION:3
#EXT-X-INDEPENDENT-SEGMENTS
#EXT-X-MEDIA-SEQUENCE:1
#EXT-X-TARGETDURATION:8
#EXT-X-PROGRAM-DATE-TIME:2023-10-03T20:14:17.821796+00:00
#EXTINF:4, no desc
live-audio=126470-video=1600215-424091019.ts?bpkio_serviceid=a3eb043e7bf775ded159a8940be5e720&bpkio_sessionid=10c0b51d66-005aea96-dc0b-40d4-8004-090bc0949313
#EXTINF:4, no desc
live-audio=126470-video=1600215-424091020.ts?bpkio_serviceid=a3eb043e7bf775ded159a8940be5e720&bpkio_sessionid=10c0b51d66-005aea96-dc0b-40d4-8004-090bc0949313
#EXT-X-DISCONTINUITY
#EXT-X-PROGRAM-DATE-TIME:2023-10-03T20:14:43.000000+00:00
#EXTINF:8,
../../../AVOD/TOS-original-24fps-1080p/conditioned/video/1600000/ts/segment_0.ts?bpkio_serviceid=a3eb043e7bf775ded159a8940be5e720&bpkio_sessionid=10c0b51d66-005aea96-dc0b-40d4-8004-090bc0949313
#EXTINF:8,
../../../AVOD/TOS-original-24fps-1080p/conditioned/video/1600000/ts/segment_1.ts?bpkio_serviceid=a3eb043e7bf775ded159a8940be5e720&bpkio_sessionid=10c0b51d66-005aea96-dc0b-40d4-8004-090bc0949313

This means that requests for media segments of the alternate content are sent to https://[uniqueid].cloudfront.net/AVOD/...

Assuming that all those replacement assets are under the same root folder AVOD, the path pattern /AVOD/* will provide a clean way to differentiate those segments from the original stream segments, and we can create a new behaviour in the distribution accordingly.

Cache, origin request and CORS policies will typically be similar to those for the default origin.

Dynamic Ad Insertion

Ads (and/or slate) prepared by broadpeak.io

If you use broadpeak.io for ad insertion, and use the transcoding feature, broadpeak.io will originate the prepared ads (and gap filler if relevant) through stream.broadpeak.io. In this case, the path to the prepared content will start with /bpkio-jitt/.

We can therefore create a new behavior to route the ad segment requests:

  • Path pattern: /bpkio-jitt/*
  • Origin: stream.broadpeak.io (the same one created in the first step)
  • Caching Policy: CachingOptimized
  • Origin Request Policy: Elemental-MediaTailor-PersonalizedManifests
  • Response Headers Policy: CORS-With-Preflight

Server-Side Ad Tracking

If you enable SSAT, broadpeak.io will proxy requests to relevant segments (via stream.broadpeak.io again). The path starts with bpk-sst.

Once again, an additional behavior can be created for that use case:

  • Path pattern: bpk-sst/*
  • Origin: stream.broadpeak.io
  • Caching Policy: CachingDisabled
  • Origin Request Policy: Elemental-MediaTailor-PersonalizedManifests
  • Response Headers Policy: CORS-With-Preflight

📘

Packaged ads?

If you are not using broadpeak.io to transcode and package your ads, and/or you don't use SSAT, this section does not apply. Instead, configure the origin and behaviour in the same way as in the previous section.

 Other Rules

 HLS Subtitles

If your content is in HLS and has WebVTT subtitles, but the alternate content (replacement content, or ads) does not, broadpeak.io will insert references to an empty VTT file (/empty.webvtt).

You need to add this one to your distribution as well:

  • Path pattern: /empty.webvtt
  • Origin: stream.broadpeak.io
  • Caching Policy: CachingOptimized
  • Origin Request Policy: (not necessary)
  • Response Headers Policy: CORS-With-Preflight

 Summary

So, a full list of behaviors for a distribution that combines all this will look something like the following:

For an initial configuration, this ought to do it.