Enabling Blackout with ESNI API

This Playbook focuses on using ESNI API calls supported by Broadpeak.io. ESNI (Event Schedule Notification Interface) is mostly used in the broadcast world and is related to SCTE 224 standard. You would be interested in this playbook if :

  • you are using a Content Management System (CMS) or a Scheduler that works with the SCTE 224 ESNI standard
  • you want to trigger Content Replacement slots in broadpeak.io from your CMS

We will see here how to create replacement slots related to a content replacement service, assuming we have already created sources and a relevant service in broadpeak.io. If you wonder how, please go and visit this Playbook This use case will deal with a HLS version of a live channel. The steps would be the same with a DASH version.

Prequisites

To fully test this playbook, you will need:

  • A content replacement service created in broadpeak.io, based on 2 sources, one live streaming source, and another default replacement source.
  • An API key, generated from the webapp, to be able to send API requests from you environment or from an API tool such as Postman.

Context

Let's take a concrete example for our use case : we are a media service provider. We deliver many channels on our user TV portal: our subscribers can access from their home TV box, their mobile app or from our web portal. Among this offer, we have sport channels that deliver games from all over the world. These channels are particularly sensitive to broadcast rights on special events : we can’t deliver any games for our whole user base, we have to adapt punctually our offer.

For instance, a sport channel called Sport45 will display a soccer game for which we don't have de broadcast rights. The game takes place in Dallas on March 14th 2023 at 2pm (i.e. 14:00 GMT+06:00) and will last 1 hour and 40 minutes.

Previously, I have set in Broadpeak.io a Content replacement service called SportBlackout_HLS, based on 2 sources and an output url:

  • a main HLS Live source : the targetted channel stream, which is named Sport45_HLS in our broadpeak.io environment
  • a default replacement source : an Asset which displays a static image with a message explaining the content is not accessible. We named it SportBlackoutScreen_HLS

The output url of my service is : https://stream.broadpeak.io/801c14f07f9xxx22802ad4b17127a0a0/sport45_HLS/output/index.mpd

My Content Replacement service is ready to operate as soon as I set a slot.

Create a slot

To create a slot with the ESNI API, we need to send an HTTP PUT request to the following endpoint https://api.broadpeak.io/v1/esni/media/mediapoint.

The body of the request uses the name of the service, the timestamp of slot's start time and the duration of the slot. The simplest request to set our slot will be as follow :

PUT : <https://api.broadpeak.io/v1/esni/media/mediapoint>

<Media href="SportBlackout_HLS">
  <MediaPoint id="mediapoint" matchTime="2023-03-14T14:00:00+06:00" expectedDuration="PT1H40M">
    <Apply>
      <Policy id="1">
        <ViewingPolicy id="1">
           \<action:Content>urn:scte:224:action:blackout\</action:Content>
        </ViewingPolicy>
      </Policy>
    </Apply>
  </MediaPoint>
</Media>

Note that Apply, Policy, Viewing policy and Action are mandatory in an ESNI request, although we don't use them here. \<action:Content>urn:scte:224:action:blackout\</action:Content> is a standard line. Your attention has to be focused on Media href, which identifies the targetted service by its name, and on matchTime and expectedDuration for timing settings

Now you are able to create a slot. But do you know that some more options are available when creating a slot? Segmenting your subscriber base for content replacement slots, choosing specific contents for replacement slots, or even managing DASH and HLS versions in a single API call. Check the following part of this article to learn more about ESNI API.

Create a slot for a segmented subscribers base

Create a slot for a specific Audience

Let’s take our previous soccer game example and assume that finally, we have the broadcasting rights, except for people watching from their mobile devices. Then here the intent is to forbid mobile devices from having access to the content; they are ultimately the target Audience we want to address. For the sake of this example we will use the keyword “Mobile” to identify this Audience.

This keyword must be used by the subscriber’s player as a query parameter to fetch the video from streaming servers. This audience must also be declared in broadpeak.io as a Category to recognize the request and prevent the user from having access to the original content during the defined period of time. To know more about category use, please visit

Here is the request to set the desired slot.

PUT : <https://api.broadpeak.io/v1/esni/media/mediapoint>

<Media href="SportBlackout_HLS">
  <MediaPoint id="mediapoint" matchTime="2023-03-14T14:00:00+06:00" expectedDuration="PT1H40M">
    <Apply>
      <Policy id="1">
        <ViewingPolicy id="1" match="ANY">
           <Audience id="Mobile">           
             </Audience>
           <action:Content>urn:scte:224:action:blackout</action:Content>
        </ViewingPolicy>
      </Policy>
    </Apply>
  </MediaPoint>
</Media>

This request creates a slot for the Audience “Mobile”. Compared to the previous example with no audience, this requests has a Audience id defined as Mobile. In broadpeak.io, this audience corresponds to one of the defined category. The URL that will deliver the stream with the blackout slot should then contain the query parameter ?category=mobile” Here is an example of how it must look like :

https://origin.video/801c14f07f9xxx22802ad4b17127a0a0/sport45/output/index.m3u8**?category=mobile**

If it’s the only slot that has been created, any other value of category= (e.g. ?category=webportal) in the requested URL will deliver the original stream, i.e. the soccer game. Same if the query parameter ?category= doesn’t exist in the requested URL.

Create a slot for Audience with zip codes

Subscriber's home Zip codes directly reflects the area where the subscribers are located. When local broadcasting rights are negociated, Zip codes are a convenient way to identify users and decide if they are located in the area where the event can be displayed or not.

Let's say we want to create a blackout slot for people living in Dallas, whatever their device. There are something like 70 different zip codes for Dallas. To avoid multiple requests (one slot creation for each zip code) and facilitate the use of the API when dealing with zip codes, broadpeak.io allows API users to define subcategories.

Thus, if we want to address an Audience that represents the whole city of Dallas, USA, we can create a slot with an Audience called Dallas which would contain more than 70 subcategories – all the existing zipcodes. As Dallas zip codes start from 75001, the request should be shaped in the following way :

PUT : <https://api.broadpeak.io/v1/esni/media/mediapoint>

<Media href="SportBlackout_HLS">
  <MediaPoint id="mediapoint" matchTime="2023-03-14T14:00:00+06:00" expectedDuration="PT1H40M">
    <Apply>
      <Policy id="1">
        <ViewingPolicy id="1">
           <Audience id="Dallas">
             <audience:zip>75001</audience:zip>
             <audience:zip>75006</audience:zip>
             <audience:zip>75007</audience:zip>
             <audience:zip>.....</audience:zip>
           </Audience>
           <action:Content>urn:scte:224:action:blackout</action:Content>
        </ViewingPolicy>
      </Policy>
    </Apply>
  </MediaPoint>
</Media>

The query parameters contained in the streaming request from the player must contain- ?zip= with the actual zipcode of the subscriber home to display the blackout replacement slot during the game:

…/index.m3u8?zip=75006

At this stage, you should be able to use ESNI API calls to set blackout replacement slots on a live stream for a segmented user base, whether they are identified with a category or a zip code in query parameters their player is using.

Now, are you curious enough to discover more about ESNI API opportunities? Check the following chapters to know how to trigger slots for both a DASH and HLS versions in one ESNI API request. Or do you prefer to discover how you can choose the replacement content within a slot creation ?

Create a slot with a custom replacement content source

Let's see now how to set a slot related to a content replacement service, and meanwhile decide the replacement content to display during the slot. You may already know that a content replacement service is based on a live main source and a default content replacement source. This default source will be displayed for each slot of the service, if no other replacement content is designated.

However, broadpeak.io allows you to choose a different source for each replacement slot you want to create.

We are using the same context than previously. Remember, our content replacement service is based on two sources : the main one (Sport45_HLS) and the default replacement one (SportBlackoutScreen_HLS). During the game, we would like to deliver a documentary about soccer in Texas to our whole subscriber base : “Texas Soccer History”

We assume that we have already created a source as an asset in broadpeak.io, called TexasSoccerHistory_HLS. To set the expected slot, we can use this ESNI API call :

PUT : <https://api.broadpeak.io/v1/esni/media/mediapoint>

<Media href="SportBlackout_HLS">
  <MediaPoint id="mediapoint" matchTime="2023-03-14T14:00:00+06:00" expectedDuration="PT1H40M">
    <Apply>
      <Policy id="1">
        <ViewingPolicy id="1">
           <action:Content>TexasSoccerHistory_HLS</action:Content>
        </ViewingPolicy>
      </Policy>
    </Apply>
  </MediaPoint>
</Media>

You can see that the request specifies an action, which type is "content" (<action:content>), where you can define a new replacement content :

TexasSoccerHistory_HLS. In previous examples, the content was set as urn:scte:224:action:blackout, as we wanted to use the default replacement content, SportBlackoutScreen_HLS.

To add more complexity to our use case and to re-use what is described in the use case about ESNI, slots and audience, let's say now that we only want to deliver the documentary to subscribers living in Dallas, the other ones can have access to the game. Then our API ESNI request must define Audience and content replacement source.

PUT : <https://api.broadpeak.io/v1/esni/media/mediapoint>

<Media href="SportBlackout_HLS">
  <MediaPoint id="mediapoint" matchTime="2023-03-14T14:00:00+06:00" expectedDuration="PT1H40M">
    <Apply>
      <Policy id="1">
        <ViewingPolicy id="1">
           <Audience id="Dallas">
							<audience:zip>75001</audience:zip>
							<audience:zip>75006</audience:zip>
							<audience:zip>75007</audience:zip>
							<audience:zip>.....</audience:zip>
           </Audience>
           <action:Content>TexasSoccerHistory_HLS</action:Content>
        </ViewingPolicy>
      </Policy>
    </Apply>
  </MediaPoint>
</Media>

Our request specifies an audience that includes several zip codes. Any player streaming request with query parameters such as “…?zip=75001” will get the replacement content during the slot. Other zip query parameters values or no zip value won’t.

So if you think twice, using audiences & action:content with different replacement contents matching different audiences’ preferences may lead to a higher level of contextualized/personalized streams. But it's another story we'll tell you in another article :)

Create a slot for both DASH and HLS

Often, video streaming services offer 2 types of stream for a single channel: one HLS and one DASH, to deliver the video stream to different devices and configurations. In broadpeak.io, that means that you should use 2 different services for your content replacement slots. But to avoid creating 2 API slot requests for those 2 separated streams, broadpeak.io ESNI API allows you to impact both services in a single API call. One condition for that : Service’s and Sources names has to respect a certain format, which is rather simple to understand, but mandatory to respect. Let’s see how this can ease your workflow.

First, let’s have a look at what we have in our broadpeak.io account:

  • 1 service named SportBlackout_HLS
  • 1 service named SportBlackout_DASH (See? Same names except the HLS and DASH. This is simple but this is where stands the key of that chapter:)

Then a single request, written as follow will create a slot on each of the 2 services :

PUT : <https://api.broadpeak.io/v1/esni/media/mediapoint>

<Media href="SportBlackout">
  <MediaPoint id="mediapoint" matchTime="2023-03-14T14:00:00+06:00" expectedDuration="PT1H40M">
    <Apply>
      <Policy id="1">
        <ViewingPolicy id="1">
           <action:Content>urn:scte:224:action:blackout</action:Content>
        </ViewingPolicy>
      </Policy>
    </Apply>
  </MediaPoint>
</Media>

Thus, we don’t add _HLS and _DASH after the characters SportBlackout and doing that allows us to target both services for our slot creation request.

If you want to know more about this please check that page.

And what if we want to change the default replacement content for our slot ? To keep our example used in a former chapters of this article, we want to use a documentary during our slot, instead of the default replacement content defined in our services. Previously, we declared two additional assets sources in broadpeak.io:

  • TexasSoccerHistory_HLS
  • TexasSoccerHistory_DASH

Guess what ? One single API call is enough to create those two slots. Here it is :

PUT : <https://api.broadpeak.io/v1/esni/media/mediapoint>

<Media href="SportBlackout">
  <MediaPoint id="mediapoint" matchTime="2023-03-14T14:00:00+06:00" expectedDuration="PT1H40M">
    <Apply>
      <Policy id="1">
        <ViewingPolicy id="1">
           <action:Content>TexasSoccerHistory</action:Content>
        </ViewingPolicy>
      </Policy>
    </Apply>
  </MediaPoint>
</Media>

Same trick: if you skip _HLS and _DASH from the replacement source name, broadpeak.io will choose the relevant version for the content replacement slot.

& that’s it ! Thanks for reading.