HLS playlist manipulation

Content substitution and content insertion, in HLS, with illustrated examples

📘

Terminology

This page uses the term "alternate content" to mean any content that is used to replace original content or be inserted or stitched to original content.

The term "variant" is also used to refer to HLS (video) Variant Streams

The terms "rendition" and "track" are also used interchangeably to refer to any of the streams in the HLS manifest, whether as (video) variant streams or (audio) media.

📘

About the examples

All examples in this page are made up and shortened for the purpose of illustration. They are not functional manifests, and will differ in a number of respects to manifests that will actually be used as Source or generated by broadpeak.io.

White space and comment lines are also freely added that would like not be found in real manifests.

Multivariant Playlist

The Multivariant Playlist in HLS lists the available streams, and a typical player will select one or several to play.

Many HLS players are very sensitive to changes in a stream and more specifically to changes in decoding parameters (such as codec, profile and level).

The broadpeak.io platform is designed to maintain the best quality of experience (QoE), and for this reason, it only performs manifest manipulation if the tracks in the original content and any content that needs to be replaced or inserted are fairly compatible.

Matching video and audio renditions

To do so, broadpeak.io looks for compatibility between the renditions of the original and alternate content. The sections below highlight a set of rules for determining this compatibility.

Video with multiplexed audio

📘

Summary: multiplexed HLS audio and video tracks

  1. Codecs. broadpeak.io looks at the CODECS attribute on variants to determine compatibility between original and alternate content. No codec compatibility = no contextualisation. Transcoding profiles are also taken into account in the comparison.
  2. Languages. broadpeak.io looks at LANGUAGE compatibility between original and replacement content. If languages are different, variants with no correspondence in the alternate content are matched with the DEFAULT=YES compatible variant.
  3. Bandwidth. If there is codec compatibility, broadpeak.io looks at the BANDWITDH value, and matches a variant with the variant that has the closest bandwidth value in the alternate content.

1. Codec compatibility

Codec information is retrieved from the attribute CODECS in the #EXT-X-STREAM-INF tag that defines the variant stream:

#EXT-X-STREAM-INF:BANDWIDTH=833000,AVERAGE-BANDWIDTH=758000,CODECS="mp4a.40.2,avc1.4D401F",RESOLUTION=426x240,FRAME-RATE=25,AUDIO="audio-aac",CLOSED-CAPTIONS=NONE
video-with-audio-1.m3u8

Since audio and video are multiplexed, that attribute contains two codec strings separated by a comma, which describe the audio and the video codecs of the track. The compatibility check is performed on both audio and video codecs, so if a track is compatible with the video codec but not with the audio codec - or vice-versa - it is considered not compatible. Each codec string needs to be exactly the same (case insensitive) between original and alternate content to be a match.

If at least one variant stream in the original content does not have a compatible match in the alternate content, the whole manifest is considered incompatible, and manifest manipulation will not occur. In this case, the original manifest is returned.

If all variants have at least one compatible match, the next compatibility rule is evaluated.

2. Language compatibility

It is common, in particular in Content Replacement applications, to perform content switches from national or international content to regional content. Therefore, original and alternate content often contain different languages and possible also contain a different number of standalone audio tracks. Here as well, compatibility needs to be evaluated.

broadpeak.io looks at the language of the codec-compatible remaining variants, and uses the audio rendition group identifier in each variant (the AUDIO attribute) to identify which languages renditions are available. In the example below, that identifier is audio-aac.

# AUDIO groups
# Primary audio, multiplexed with video
#EXT-X-MEDIA:TYPE=AUDIO,GROUP-ID="audio-aac",NAME="English",LANGUAGE="eng",DEFAULT=YES,CHANNELS="2"
# Alternate standalone audio rendition
#EXT-X-MEDIA:TYPE=AUDIO,GROUP-ID="audio-aac",NAME="Spanish",LANGUAGE="spa",CHANNELS="2",URI="spanish-audio.m3u8"

# Variants
#EXT-X-STREAM-INF:BANDWIDTH=833000,AVERAGE-BANDWIDTH=758000,CODECS="mp4a.40.2,avc1.4D401F",RESOLUTION=426x240,FRAME-RATE=25,AUDIO="audio-aac",CLOSED-CAPTIONS=NONE
video-with-english-audio.m3u8

Each audio rendition in the group describes the language of each available audio track through the attribute LANGUAGE; this is the value that broadpeak.io uses to search for the playlist with a language match in the alternate content.

📘

Note

broadpeak.io does not maintain an ISO-639-1 to ISO-639-2 mapping table. Please make sure both original and alternate content use the same language code syntax or they will be considered incompatible.

If there is no exact language match, broadpeak.io attempts to match the audio tracks from the original content to the audio variant which has the DEFAULT=YES attribute within the audio group.

Indeed, it is better for a player to have an continuous audio track even if not in the same language, than no audio at all - which in most cases would result in an error during playout.

From here, choices need to be made, depending on how many audio renditions are in both original and alternate content

2.a. More audio renditions in the original content than in the alternate content

Let's assume a single audio rendition group with two languages in your original content, English (en) - set to be the default - and Spanish (sp), and a single audio rendition group with a single audio track in English (en) in your alternate content. For the sake of the example let's also assume that both audio rendition groups are codecs compatible. Graphically, this would look like this:

982

Since there is no Spanish audio track equivalent in the alternate content, broadpeak.io looks for a codec-compatible track that has the attribute DEFAULT=YES - in our case it is the English track - and considers it as a match.

Once the media manifest is manipulated, the variant with the Spanish audio track will reference English media segments from the alternate content.

982
2.b. Fewer audio renditions in the original content than in the alternate content

When the alternate content has more audio tracks than the original content, broadpeak.io only matches the tracks that are present in the content source manifest. In the event that there is no language match, broadpeak.io uses a codec-compatible audio track with the DEFAULT=YES attribute as a match.

If we take a new example in which the original content contains a single audio rendition group with a single language, English (en) and the alternate content contains a single audio rendition group with two audio tracks, English (en) and Spanish (sp). Let's again assume for the sake of the example that both audio rendition groups are codec-compatible. Graphically, this would look like this:

982

In this example, since there is no Spanish audio track in the multivariant manifest of the original content, broadpeak.io only matches English audio tracks together. The Spanish track will not be referenced in the output manifest.

491

3. Bandwidth similarity

Once codec and language compatibility rules have successfully passed, the list of remaining possibilities for a match should be reduced. broadpeak.io now compares the bandwidth information of the codec-compatible variant streams in both manifests.

To preserve the best quality of experience, the system looks for the best variants from the alternate content to match each of the variants of the original content.

📘

Bandwidth as a guide

Bandwidth commonly is - for a same codec and profile - directly linked to encoding parameters such as resolution. Different assets encoded with the same parameters and resolution tend to result in similar bitrate and therefore the same impact over the network bandwidth.

The variant stream of the alternate content - which is codec-compatible - that has the smallest difference in bandwidth with the variant of the original content is selected as a match.

The bandwidth of a variant is retrieved from the attribute BANDWITH on the #EXT-X-STREAM-INF tag.

3.a. When the alternate content has more variants

In general, it is best to avoid a difference in the number of variants available between the original content manifest and the alternate content manifest. There are viable use cases however, for instance when content in an SD stream with HD content.

In case the alternate content has more variants than the original content, broadpeak.io matches all renditions of the original content with alternate compatible renditions based on bandwidth, and ignores any additional rendition in the alternate content for which there is no suitable an equivalent in the original content. It may result in the renditions of the alternate content with highest or lowest qualities to not be used.

Let's take the following example in which we have two assets, SD as original content and HD as alternate content. All renditions in the original content have at least one codec-compatible rendition in the alternate content.

Here, the 3 lowest variants of the SD content are matched to the 3 lowest variants of the HD content. The highest variant of the alternate content is simply ignored.

902
3.b. When the alternate content has fewer variants

Here as well, it is recommended to avoid such a scenario and adapt the original and alternate streams to preserve quality of experience. If this is not possible, here is how it is handled.

In case the alternate content has more variants than the original content, broadpeak.io will still choose a match for every variant stream of the original content from the codec-compatible variants in the alternate content, selecting the one with the smallest difference in bandwidth. A variant from the alternate content will therefore likely become a match for multiple variants of the original content.

Let's take the following example where we have two assets, HD as original content and SD as alternate content. All renditions in the original content have at least one codec-compatible rendition in the alternate content.

With a switch from the HD content to the SD content, the highest variant of the alternate content (720p) becomes a match for both 1080p and 720p variants of the original content, despite the significant difference in bitrate.

904

Distinct audio and video playlists

When working with separate audio and video tracks, things are more or less the same as when audio and video are multiplexed (the previous section), except that the compatibility check is applied on audio and video separately.

📘

Summary: separate HLS audio and video tracks

1.a. Video Codecs. broadpeak.io looks at video CODECS compatibility between the video variant of the original and those of the alternate content. No codec compatibility = no contextualization

1.b. Video Bandwidth. If there is codec compatibility, broadpeak.io looks at the BANDWIDTH value, and matches a video variant of the original content with the video variant of the alternate content that has the closest bandwidth value.

2.a. Audio Codecs. broadpeak.io looks at the audio CODECS compatibility between the original and alternate content. No codec compatibility = no contextualization

2.b. Audio Languages. broadpeak.io looks at LANGUAGE compatibility between audio variants of the original content and those of the alternate content. If the languages are different, variants with no correspondence in the alternate content are matched with the DEFAULT=YES compatible variant.

1.a. Video codec compatibility

broadpeak.io looks for compatibility between the variants of the original and alternate content by parsing the CODECS attribute, and extracting from it the codec string of the original content's video variants then attempts to find a variant in the alternate content manifest with the same string (case insensitive).

#EXT-X-STREAM-INF:BANDWIDTH=833000,AVERAGE-BANDWIDTH=758000,CODECS="mp4a.40.2,avc1.4D401F",RESOLUTION=426x240,FRAME-RATE=25,AUDIO="audio-aac",CLOSED-CAPTIONS=NONE
video-only-1.m3u8

Even when audio and video are separated, the codec attribute will generally contain two values separated by a comma, with the audio and the video codecs of the combined tracks that the client needs to play together. The compatibility check is only performed on video codecs at this stage.

If at least one video variant in the original content does not have a compatible match in the alternate content, the whole manifest is considered incompatible, and manifest manipulation will not occur. In this case, the original manifest is returned.

If all variants have at least one a compatible match, the next compatibility rule is evaluated.

1.b Video bandwidth similarity

Once the codec compatibility rule has successfully passed, the list of remaining possibilities for a match should be reduced. broadpeak.io now compares the bandwidth information of the codec-compatible variant streams in both manifests.

To preserve the best quality of experience, the system looks for the best variant from the alternate content to match to each of the variants of the original content, by comparing the bandwidth information, extracted from the BANDWIDTH attribute of the variant.

📘

Bandwidth as a guide

Bandwidth commonly is - for a same codec and profile - directly linked to encoding parameters such as resolution. Different assets encoded with the same parameters and resolution tend to result in similar bitrate and therefore the same impact over the network bandwidth.

The codec-compatible variant of the alternate content which has the smallest difference in bandwidth with the variant of the original content is selected as a match.

2.a Audio codec compatibility

When audio tracks are in dedicated variants, as is becoming more common (and a constraint when using fMP4/ISOBMFF), each audio variant of the original content must have a match with the alternate content. In a similar way as for video variant streams, the audio codec is retrieved from the video variant #EXT-X-STREAM-INF and compared between original and alternate content.

In the example below, the first string before the comma in CODECS="mp4a.40.2,avc1.4D401F" is the audio codec, which is valid for all audio tracks contained in the audio rendition group (declared with AUDIO="audio-aac").

#EXT-X-STREAM-INF:BANDWIDTH=833000,AVERAGE-BANDWIDTH=758000,CODECS="mp4a.40.2,avc1.4D401F",RESOLUTION=426x240,FRAME-RATE=25,AUDIO="audio-aac",CLOSED-CAPTIONS=NONE
video-only-1.m3u8

2.b Audio language compatibility

The next step is to determine audio language compatibility.

The rules are the same as for multiplexed audio and video variants, explained in a previous section

If there is no exact language match, broadpeak.io attempts to match the audio tracks from the original content to the alternate audio variant which has the DEFAULT=YES attribute within the audio group.

📘

Note

broadpeak.io does not maintain an ISO-639-1 to ISO-639-2 mapping table. Please make sure both original and alternate content use the same language code syntax or else it will be considered incompatible.

Matching subtitle renditions

In a similar way to audio languages, subtitles languages sometimes also differ between original and alternate content. broadpeak.io performs compatibility checks in order to make sure formats are compatible and languages are properly matched.

1. Format compatibility

broadpeak.io does not perform any format checks. It is assumed that the subtitles used are WebVTT on both original and alternate content.

2. Language compatibility

broadpeak.io looks for the language of the subtitle rendition(s) in the subtitle rendition group linked to the video variant (indicated by SUBTITLES). In the example below, the subtitle rendition id is textstream.

# Audio groups
#EXT-X-MEDIA:TYPE=AUDIO,GROUP-ID="audio-aacl-63",NAME="English",LANGUAGE="eng",AUTOSELECT=YES,DEFAULT=YES,CHANNELS="2"

# Subtitle renditions
#EXT-X-MEDIA:TYPE=SUBTITLES,GROUP-ID="textstream",NAME="English",LANGUAGE="eng",AUTOSELECT=YES,DEFAULT=YES,URI="replacementcontent-textstream_7208961_eng=8000.m3u8"
#EXT-X-MEDIA:TYPE=SUBTITLES,GROUP-ID="textstream",NAME="Spanish",LANGUAGE="spa",AUTOSELECT=YES,URI="replacementcontent-textstream_12451841_spa=8000.m3u8"

# Video variants
#EXT-X-STREAM-INF:BANDWIDTH=833000,AVERAGE-BANDWIDTH=758000,CODECS="mp4a.40.2,avc1.4D401F",RESOLUTION=426x240,FRAME-RATE=25,AUDIO="audio-aacl-63",SUBTITLES="textstream",CLOSED-CAPTIONS=NONE
ARTE-audio_63340_eng=63200-video=643600.m3u8

# Audio variants
#EXT-X-STREAM-INF:BANDWIDTH=83000,AVERAGE-BANDWIDTH=75000,CODECS="mp4a.40.2",AUDIO="audio-aacl-63",SUBTITLES="textstream"
ARTE-audio_63340_eng=63200.m3u8

Each subtitle rendition (# EXT-X-MEDIA:TYPE=SUBTITLES) in the group includes a language in the LANGUAGE attribute. The language value retrieved from each subtitle track in the original content is compared to the language values available in the alternate content to find a match.

If no match is found, broadpeak.io looks for the DEFAULT=YES subtitle track to perform a match. Therefore, languages might not be the same, but playback stability is preserved.

Matching image tracks

The same logic as for Video variant compatibility check is applied when working with I-frame playlists (#EXT-X-I-FRAMES-ONLY). Since they do not have audio, languages compatibility checks do not apply.

  1. Codecs need to be compatible between the I-frame renditions of the original content, and those of the alternate content.
  2. After which I-frame renditions are selected based on the smallest difference in bandwidth

Media Playlists

When matches have been found for all video, audio, subtitles and I-frame renditions of the original content, broadpeak.io will now manipulate the corresponding Media Playlists to insert or substitute the alternate content.

Original media references inside the multivariant manifest are not modified - though some query parameters are added in the variant path.

Content Substitution

📘

Terminology

For the sake of simplicity in this section, we will call an "event" anything that can cause a manifest to be manipulated, whether it is a slot scheduled, a SCTE-35 marker in the source, or an out-of-band ad-break API trigger.

Substituted segments

If there is an "event" in the DVR window, broadpeak.io will replace media URIs which reference the original media segments in the media playlists, with URIs that reference substitute media segments, for the duration of the "event".

The media playlist will show a switch from segments in the the original source to segments of a substitute source from the start of the "event", and after it is finished, a switch back from the substitute content to the original content at the end of the slot.

Discontinuities

Each content switch is represented in the manifest by a discontinuity tag #EXT-X-DISCONTINUITY:

  1. from the original source to the substitute source first,
  2. then back to the original source after 12s (ie. 3 segments in this case).

Media sequence

When a new streaming session is opened, broadpeak.io will set the media sequence value of the first delivered media playlist to the value 1, regardless of the original value. The media sequence value is then incremented from there throughout the streaming session, as the media playlist gets updated.

#EXT-X-MEDIA-SEQUENCE:1

This will happen in all cases, even when there is no "event" in the DVR Window.

Example

Let's illustrate these changes by looking at one of the media playlists of a live HLS manifest:

#EXTM3U                                                                                                              
#EXT-X-VERSION:5
#EXT-X-INDEPENDENT-SEGMENTS
#EXT-X-MEDIA-SEQUENCE:10
#EXT-X-TARGETDURATION:4
#EXT-X-PROGRAM-DATE-TIME:2021-01-01T01:10:00.000000+00:00
#EXTINF:4, no desc
video/segment-41.ts
#EXTINF:4, no desc
video/segment.ts
#EXTINF:4, no desc
video/segment.ts
#EXTINF:4, no desc
video/segment.ts
#EXTINF:4, no desc
video/segment.ts
#EXTINF:4, no desc
video/segment.ts
#EXTINF:4, no desc
video/segment.ts
#EXTINF:4, no desc
video/segment.ts
#EXTINF:4, no desc
video/segment.ts

An "event" is triggered that dictates a switch from the original source to a substitute content after 16s in the DVR window. The substitute content lasts for 12s, after which the stream switches back to the original content.

The resulting manifest will have 2 content switches, and therefore 2 discontinuities

#EXTM3U                                                                                                              
#EXT-X-VERSION:5
#EXT-X-INDEPENDENT-SEGMENTS
#EXT-X-MEDIA-SEQUENCE:1
#EXT-X-TARGETDURATION:4
#EXT-X-PROGRAM-DATE-TIME:2021-01-01T01:10:00.000000+00:00
#EXTINF:4, no desc
video/segment-41.ts
#EXTINF:4, no desc
video/segment-42.ts
#EXTINF:4, no desc
video/segment-43.ts
#EXTINF:4, no desc
video/segment-44.ts

# switch to substituted/inserted content
#EXT-X-DISCONTINUITY
#EXT-X-PROGRAM-DATE-TIME:2011-01-01T01:10:16.000000+00:00
#EXTINF:4, no desc
../../../../replacement_content/hls/audio=129117-video=633990-190.ts
#EXTINF:4, no desc
../../../../replacement_content/hls/audio=129117-video=633990-191.ts
#EXTINF:4, no desc
../../../../replacement_content/hls/audio=129117-video=633990-192.ts

# switch back to original content
#EXT-X-DISCONTINUITY
#EXT-X-PROGRAM-DATE-TIME:2011-01-01T01:10:28.000000+00:00
#EXTINF:4, no desc
video/segment-48.ts
#EXTINF:4, no desc
video/segment-49.ts

📘

Program Date Time

Notice how an EXT-X-PROGRAM-DATE-TIME is also inserted after each to discontinuity, to allow the player to re-map the stream time to the wall clock

Failed contextualization

In the event that the manifest contextualization could not be performed, the media playlist will show a discontinuity tag at the beginning and the end of the portion of the content that was supposed to be substituted. Depending the feature used, the media segment will keep referencing the original content (Dynamic Ad Insertion or Virtual Channel) or reference invalid segments with the mention "BLACKOUTED/INVALID" (Content Replacement).

This may happen:

  1. If no match could be found for at least one rendition of the original content within the substitution content
  2. If the manifest of the substitution content is not yet available or cannot be parsed.

Content Insertion

Inserted segments

When performing insertion, broadpeak.io will insert new media segments among existing segments in the stream.

The media playlist will therefore show a switch from segment URIs in the original source to different segments URIs for the new content, before going back to the next segment from the original source.

Based on the type of ad inserted (pre-roll, mid-roll or post-roll), the insertion of new content may happen right at the start of the playlist, in the middle of it, or at the end of it.

Discontinuities

Each content switch is represented in the manifest by a discontinuity tag #EXT-X-DISCONTINUITY:

  1. from the original source to the new content first,
  2. then back to the original content, where it left off.

Example

To illustrate these changes, let's look at one of the media playlists for a VOD asset 24 seconds long:

#EXTM3U                                                                                                              
#EXT-X-VERSION:5
#EXT-X-INDEPENDENT-SEGMENTS
#EXT-X-MEDIA-SEQUENCE:1
#EXT-X-TARGETDURATION:4
#EXTINF:4, no desc
segment-01.ts
#EXTINF:4, no desc
segment-02.ts
#EXTINF:4, no desc
segment-03.ts
#EXTINF:4, no desc
segment-04.ts
#EXTINF:4, no desc
segment-05.ts
#EXTINF:4, no desc
segment-06.ts
#EXT-X-ENDLIST

Following the successful retrieval of ad information, three insertion of content are performed, at the beginning of the asset, after 12 seconds, and at the end. All the inserted ads are 8 seconds. Here is a visual representation of how it would look:

Notice how the total duration of the stream is now 48 seconds and contains 4 discontinuities

#EXTM3U                                                                                                              
#EXT-X-VERSION:5
#EXT-X-INDEPENDENT-SEGMENTS
#EXT-X-MEDIA-SEQUENCE:1
#EXT-X-TARGETDURATION:4
#EXTINF:4, no desc
../../bpkio-jitt/hls/pre-roll/audio=129117-video=633990-01.ts
#EXTINF:4, no desc
../../bpkio-jitt/hls/pre-roll/audio=129117-video=633990-02.ts

#EXT-X-DISCONTINUITY
#EXTINF:4, no desc
segment-01.ts
#EXTINF:4, no desc
segment-02.ts
#EXTINF:4, no desc
segment-03.ts
#EXTINF:4, no desc

#EXT-X-DISCONTINUITY
../../bpkio-jitt/hls/mid-roll/audio=129117-video=633990-01.ts
#EXTINF:4, no desc
../../bpkio-jitt/hls/mid-roll/audio=129117-video=633990-02.ts

#EXT-X-DISCONTINUITY
#EXTINF:4, no desc
segment-04.ts
#EXTINF:4, no desc
segment-05.ts
#EXTINF:4, no desc
segment-06.ts

#EXT-X-DISCONTINUITY
#EXTINF:4, no desc
../../bpkio-jitt/hls/post-roll/audio=129117-video=633990-01.ts
#EXTINF:4, no desc
../../bpkio-jitt/hls/post-roll/audio=129117-video=633990-02.ts
#EXT-X-ENDLIST

Dealing with DRM

Depending on your business needs, there is a good chance you are dealing with encrypted HLS sources, whether they are your original source or substituted or inserted content. broadpeak.io allows you to switch between encrypted or unencrypted sources, whether you are using AES-128 or SAMPLE-AES encryption.

HLS uses the #EXT-X-KEY tag to provide encryption information to the players. The HLS specification indicates that for any Media segment, the most recent EXT-X-KEY applies. Therefore, in the manifest generated by broadpeak.io, one such tag will be added after every discontinuity.

Example

Let's re-use our prior example of an HLS live stream with substitution and assume that the stream is encrypted, but the substitution is free-to-air content. In this case, the manifest might look like this:

#EXTM3U                                                                                                              
#EXT-X-VERSION:5
#EXT-X-INDEPENDENT-SEGMENTS
#EXT-X-MEDIA-SEQUENCE:1
#EXT-X-TARGETDURATION:4

#EXT-X-PROGRAM-DATE-TIME:2021-01-01T01:10:00.000000+00:00
# Encryption info for the source content 
#EXT-X-KEY:METHOD=AES-128,URI="https://keygenerator.com/sourcecontent/dummydrm/HLS/aes128.key",IV=0x73fbe3277bdf0bfc5217125bde4ca589
#EXTINF:4, no desc
video/segment-41.ts
#EXTINF:4, no desc
video/segment-42.ts
#EXTINF:4, no desc
video/segment-43.ts
#EXTINF:4, no desc
video/segment-44.ts

#EXT-X-DISCONTINUITY
#EXT-X-PROGRAM-DATE-TIME:2011-01-01T01:10:16.000000+00:00
# No encryption for substituted/inserted content
#EXT-X-KEY:METHOD=NONE
#EXTINF:4, no desc
../../../../replacement_content/hls/audio=129117-video=633990-190.ts
#EXTINF:4, no desc
../../../../replacement_content/hls/audio=129117-video=633990-191.ts
#EXTINF:4, no desc
../../../../replacement_content/hls/audio=129117-video=633990-192.ts

#EXT-X-DISCONTINUITY
#EXT-X-PROGRAM-DATE-TIME:2011-01-01T01:10:28.000000+00:00
# Encryption info for the source content 
#EXT-X-KEY:METHOD=AES-128,URI="https://keygenerator.com/sourcecontent/dummydrm/HLS/aes128.key",IV=0x73fbe3277bdf0bfc5217125bde4ca589
#EXTINF:4, no desc
video/segment-48.ts
#EXTINF:4, no desc
video/segment-49.ts

Note how the EXT-X-KEY is set to METHOD=NONE for the non-encrypted content, and the key for the encrypted content is repeated after the discontinuity that switches back to it.

If the replaced or inserted content is also encrypted (whether or not with its own encryption keys), that specific information will be used in the EXT-X-KEY:

#EXTM3U                                                                                                              
#EXT-X-VERSION:5
#EXT-X-INDEPENDENT-SEGMENTS
#EXT-X-MEDIA-SEQUENCE:1
#EXT-X-TARGETDURATION:4

#EXT-X-PROGRAM-DATE-TIME:2021-01-01T01:10:00.000000+00:00
# Encryption info for the source content 
#EXT-X-KEY:METHOD=AES-128,URI="https://keygenerator.com/sourcecontent/dummydrm/HLS/aes128.key",IV=0x73fbe3277bdf0bfc5217125bde4ca589
#EXTINF:4, no desc
video/segment-41.ts
#EXTINF:4, no desc
video/segment-42.ts
#EXTINF:4, no desc
video/segment-43.ts
#EXTINF:4, no desc
video/segment-44.ts

#EXT-X-DISCONTINUITY
#EXT-X-PROGRAM-DATE-TIME:2011-01-01T01:10:16.000000+00:00
# Encryption for inserted/substituted content
#EXT-X-KEY:METHOD=AES-128,URI="https://otherkeygenerator.com/othercontent/dummydrm/HLS/aes128.key",IV=0xA30FE123ECBF1BE323A775A119C553BC
#EXTINF:4, no desc
../../../../replacement_content/hls/audio=129117-video=633990-190.ts
#EXTINF:4, no desc
../../../../replacement_content/hls/audio=129117-video=633990-191.ts
#EXTINF:4, no desc
../../../../replacement_content/hls/audio=129117-video=633990-192.ts

#EXT-X-DISCONTINUITY
#EXT-X-PROGRAM-DATE-TIME:2011-01-01T01:10:28.000000+00:00
# Encryption info for the source content 
#EXT-X-KEY:METHOD=AES-128,URI="https://keygenerator.com/sourcecontent/dummydrm/HLS/aes128.key",IV=0x73fbe3277bdf0bfc5217125bde4ca589
#EXTINF:4, no desc
video/segment-48.ts
#EXTINF:4, no desc
video/segment-49.ts