Content Replacement with Cloudfront CDN
How to configure Cloudfront as a CDN for Content Replacement services
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 configure it with the AWS Cloudfront product, for a Content Replacement application.
Prerequisites
You need to be familiar with how to configure a broadpeak.io Content Replacement 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.
Specifically, 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 would look like https://stream.broadpeak.io/a3eb043e7bf775ded159a8940be5e720/bpk-tv/bpkiofficial/hlsv3/index.m3u8.
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.
For a Content Replacement service, you will need:
- Rules that define how to direct manifest requests to broadpeak.io
- Rules that define how to direct segment calls for the base live stream to its origin (ie. the server that stores that original packaged content)
- One or more rules to direct segment calls for the replacement assets to their respective origins.
Step 1 - Common Rules
The first 2 rules are common to most types of services, and with Cloudfront, they get configured in the same initial step.
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 with the samples that we provide for tests.
Some of the settings (in particular caching rules, TTL, TLS version, etc.) may also need adjusting based on your own Origin servers and typical CDN configuration needs.
Note also that the example concentrates on browser-based streaming into common web video players. Other players may require additional and specific settings.
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.
- 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.
- 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.
- 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.
- 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.
- 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.
- Finally, in the rest of that (long) screen, you should also:
- Disable security protections (unless you think this may be necessary for your situation)
- Define whether content should be distributed all over the world or in specific regions
- 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.
HLS
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
DASH
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.
Redirections
As indicated in Session management, the first call to broadpeak.io (with the serviceId in the URL path) results in an HTTP 307 redirection that changes the URL and places the serviceId in a
bpkio_serviceId
query parameter.Luckily, AWS does not cache of follow redirects, so there is nothing specific that needs configuring about it.
Step 2 - Alternate 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.
Often though, the content that is substituted into the live stream will 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.
If you have multiple origins from which content will be sourced from, you repeat those steps, and create a behaviour with an appropriate path pattern.
Other Rules
Depending on your application, there may be other redirection rules that you need to apply. Some of the common ones are:
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 for a Content Replacement application will look something like the following:
For an initial configuration, this ought to do it.
Updated 7 months ago