Dynamic Ad Insertion with Cloudfront CDN

How to configure Cloudfront as a CDN for Dynamic Ad Insertion applications

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 Dynamic Ad Insertion application (whether pre-roll, live ad replacement, or AVOD).

Prerequisites

You need to be familiar with how to configure a broadpeak.io Dynamic Ad Insertion 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.

For a Dynamic Ad Insertion service, you will need at the very least:

  • Rules that define how to direct manifest requests to broadpeak.io
  • Rules that define how to direct segment calls for the original content stream to its origin (ie. the server that stores that original packaged content)
  • Rules that define how to direct segment calls for ads

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.

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 Live Pre-Roll service has been created, the URL of which would look like https://stream.broadpeak.io/a3eb043e7bf775ded159a8940be5e720/bpk-tv/bpkiofficial/hlsv3/index.m3u8.

The configuration is fundamentally the same for Live Ad Replacement and AVOD applications. Just adapt the origin URLs accordingly.

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.

  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.

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.

Step 2 - Ads

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 Dynamic Ad Insertion service, there will be segments for ads referenced in the manifest, which will most likely come from different origins.

How you go about it depends on whether you are using broadpeak.io to process your ads or not.

Ads prepared by broadpeak.io

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

For example, the stream output with a short pre-roll 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
#EXT-X-DISCONTINUITY
#EXTINF:3, no desc
../../../bpkio-jitt/profile_15_950_91602f30ed840401/b4e039d1af7958020bc7603ac31c639e/b4e039d1af7958020bc7603ac31c639e.ism/b4e039d1af7958020bc7603ac31c639e-audio=128000-video=1600000-1.ts/bpk-sst/ClEKTzliZjMxYzdmZjA2MjkzNmFiODNhMTkwNDYwYjUwYzc0OjEwYTBiMzNhNi0zNWE1MzY5YS0yMmZiLTQ5MTAtYjJjOC1kYzdjMDMzMGRmOTQSXgpcOWJmMzFjN2ZmMDYyOTM2YWI4M2ExOTA0NjBiNTBjNzQ6MTBhMGIzM2E2LTM1YTUzNjlhLTIyZmItNDkxMC1iMmM4LWRjN2MwMzMwZGY5NDpzbG90OnByZXJvbGw?bpkio_serviceid=a3eb043e7bf775ded159a8940be5e720&bpkio_sessionid=10c0b51d66-005aea96-dc0b-40d4-8004-090bc0949313
#EXTINF:3, no desc
../../../bpkio-jitt/profile_15_950_91602f30ed840401/b4e039d1af7958020bc7603ac31c639e/b4e039d1af7958020bc7603ac31c639e.ism/b4e039d1af7958020bc7603ac31c639e-audio=128000-video=1600000-2.ts/bpk-sst/ClEKTzliZjMxYzdmZjA2MjkzNmFiODNhMTkwNDYwYjUwYzc0OjEwYTBiMzNhNi0zNWE1MzY5YS0yMmZiLTQ5MTAtYjJjOC1kYzdjMDMzMGRmOTQSXgpcOWJmMzFjN2ZmMDYyOTM2YWI4M2ExOTA0NjBiNTBjNzQ6MTBhMGIzM2E2LTM1YTUzNjlhLTIyZmItNDkxMC1iMmM4LWRjN2MwMzMwZGY5NDpzbG90OnByZXJvbGw?bpkio_serviceid=a3eb043e7bf775ded159a8940be5e720&bpkio_sessionid=10c0b51d66-005aea96-dc0b-40d4-8004-090bc0949313
#EXT-X-PROGRAM-DATE-TIME:2023-10-03T20:14:23.000000+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

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

Extending your distribution configuration to cover for this is an extension of the work done so far: we just need to add a Behavior to route requests for the ad segments towards the already configured stream.broadpeak.io origin

However, unlike requests for manifests (which go to the same origin), we need to configured the CDN to cache those requests. So, using the standard policies offered by CloudFront:

  • Caching Policy: CachingOptimized
  • Origin Request Policy: Elemental-MediaTailor-PersonalizedManifests
  • Response Headers Policy: CORS-With-Preflight

Pre-packaged Ads

If you don't use the broadpeak.io's transcoding solution, and instead have ads coming pre-packaged and from a different origin, you will need to add a new origin and a new behaviour in your CloudFront distribution.

For example if the ads come from https://my.origin-for-ads.com/packaged_ads/... you will need a new origin

And a new behaviour configured as:

  • Path pattern: /packaged_ads/*
  • Origin: my.origin-for-ads.com/packaged_ads
  • Caching Policy: CachingOptimized
  • Origin Request Policy: Elemental-MediaTailor-PersonalizedManifests
  • Response Headers Policy: CORS-With-Preflight

 Other optional rules

Depending on your application, there may be other redirection rules that you need to apply. Some of the common ones are:

Slates

If you are using slates (typically in a Live Ad Replacement scenario), and you have them transcoded and packaged by broadpeak.io, the same rule illustrated above for ad segments will take care of redirecting requests for segments of the slate.

If instead you use a pre-packaged asset, you will need to add a specific origin (potentially) and behaviour in the same way as illustrated above for pre-packaged ads.

SSAT

If you do not use broadpeak.io to transcode your ads, and you enable SSAT, broadpeak.io will proxy requests to the relevant ad segments (via stream.broadpeak.io again). The path starts with bpk-sst.

Once again, an additional behavior can be created for that use case, and those requests cannot be cached:

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

You need to make sure that this rule applies before the rule that matches /bpkio-jitt, since the SSAT call contains that path part as well. The SSAT call needs to happen first, and it redirects to a new URL that still contains / bpkio-jitt but without the bpk-sst sub-path.

 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 Dynamic Ad Insertion application (in which ads are transcoded and packaged by broadpeak.io) will look something like the following:

For an initial configuration, this ought to do it.