Menu Icon Close
App Development

Zencoder HLS Encoding with Encryption

Blog: Zencoder HLS Encoding with Encryption
June 05, 2014
By Barkat Dhillon
Reading Time: 9 minutes

How to Deliver Awesome Looking HLS Video to Desktop and Mobile

Video Content providers understand the importance of a multi-device strategy, but earlier there was no single protocol compatible with mobile (iPhone/Android/Windows) and Web. With player-side support for segmented streaming, one format has become important amongst content providers to deliver videos across mobile and web.

bmcldrev

HTTP Live Streaming, or HLS, is a video delivery protocol originally implemented by Apple as part of their iOS operating system. Zencoder’s API-driven video encoding solution makes preparing content for HLS easy, but there are still a few things worth knowing that will help content providers optimize content for their individual situation. This blog provides an overview of important information about HLS video playback in browsers and mobile devices.

If you not aware of Zencoder and its encoding API, please go through our last post on Zencoder Cloud Encoding.

HTTP Live Streaming

HTTP Live Streaming (also known as HLS) is an HTTP-based media streaming communications protocol implemented by Apple Inc. as part of their QuickTime and iOS software. It works by breaking the overall stream into a sequence of small HTTP-based file downloads, each download loading one short chunk of an overall potentially unbounded transport stream. As the stream is played, the client may select from a number of different alternate streams containing the same material encoded at a variety of data rates, allowing the streaming session to adapt to the available data rate. At the start of the streaming session, it downloads an extended M3U (m3u8) playlist containing the metadata for the various sub-streams which are available.

Since its requests use only standard HTTP transactions, HTTP Live Streaming is capable of traversing any firewall or proxy server that lets through standard HTTP traffic, unlike UDP-based protocols such as RTP. This also allows content to be delivered over widely available CDNs.
HLS also specifies a standard encryption mechanism using AES and a method of secure key distribution using HTTPS with either a device specific realm login or HTTP cookie which together provide a simple DRM system. Later versions of the protocol also provide for trick mode fast-forward and rewind and integration of subtitles. upLynk has also added the AES scrambling and base-64 encoding of the DRM content key with a 128-bit device specific key for registered commercial devices together with a sequential initialization Vector for each chunk to their implementation of the standard.
As the name implies, HLS transmits data over HTTP, which allows for various improvements over traditional streaming protocols like RTP or RTMP, including:

  • Reduced infrastructure costs
  • “Cacheability” at CDNs and other HTTP caching infrastructure
  • Less threat from proxy and firewall restrictions
  • Real-time optimization through client heuristics (adaptive bitrate)
  • Built-in redundancy
  • Simple HTML5 player implementation

 HTTP Live (Segmented) Streaming and Playlists

HTTP Live Streaming make it easy to adapt your video or audio stream to a user based on their available bandwidth, switching between streams in mid-play as the transfer speed changes. Pulling all of the pieces can be confusing, so we’ll start with how to create a few simple segmented files for different bandwidths and a playlist to provide to users on iOS devices so that they can play them.

The Segmented Files

We’ll start off by making several segmented files, each targeted at a given bandwidth. With these outputs Zencoder creates a manifest file and segmented video files, which allow the player to download each file individually as it plays the video.


{
"audio_bitrate": 128,
"audio_sample_rate": 44100,
"base_url": "s3://bluepi-encoded.s3.amazonaws.com/",
"decoder_bitrate_cap": 1500,
"decoder_buffer_size": 4000,
"filename": "sample-640k.m3u8",
"max_frame_rate": 30,
"public": false,
"type": "segmented",
"video_bitrate": 600,
"width": 400,
"format": "ts"
}

The m3u8 file created by the above output is a segmented file. Requesting this output, plus the others described below, will create multiple segmented files targeted at different bandwidths.
This output has a type of “segmented“, a format of “ts“, and defaults to a video codec of “h264“. Each segmented output will have a manifest file in M3U8 format created as its main output, and the segments will be named like the filename of the output. The segmented files will be uploaded along-side the manifest, and named with “-00001” suffixes before the extension. These outputs can be used as-is for standard streaming, simply by referencing the .m3u8 file from the client, and ensuring the segment files are available from the same location as the manifest. Multiple .m3u8 file can also be referenced by a playlist to support adaptive streaming.

The Playlist

You can create adaptive-bitrate playlists yourself. To create the playlist, just add another output to your job with a type of “playlist” and specify one or more streams with a path (that is relative to the playlist file’s location) and the bitrate of the stream in kbps (resolution and codecs can also be specified). The stream information will be formatted into a playlist and uploaded just like any other output.
This playlist file references the segmented outputs created above, specifying the bandwidth at which each should be played. Loading this file with a device that supports HLS will allow the stream to be adjusted as available bandwidth changes.
The first entry in the streams will be used when a user opens the file and is used as part of a test to determine which stream is most appropriate. The order of the other entries is irrelevant.


{
"base_url": "s3://bluepi-encoded.s3.amazonaws.com/",
"filename": "playlist.m3u8",
"public": 1,
"streams": [
{
"bandwidth": 440,
"path": "sample-440k.m3u8"
},
{
"bandwidth": 640,
"path": "sample-640k.m3u8"
},
{
"bandwidth": 240,
"path": "sample-240k.m3u8"
},
{
"bandwidth": 150,
"path": "sample-150k.m3u8"
},
{
"bandwidth": 64,
"path": "sample-64k.m3u8"
}
],
"type": "playlist"
}

The Complete Picture

Multiple outputs of different bitrates may be specified and referenced by the playlist.


{
"input": "s3://bluepi-original.s3.amazonaws.com/sample.mov",
"output": [
{
"audio_bitrate": 56,
"audio_sample_rate": 22050,
"base_url": "s3://bluepi-encoded.s3.amazonaws.com/",
"filename": "sample-64k.m3u8",
"format": "aac",
"public": 1,
"type": "segmented"
},
{
"audio_bitrate": 56,
"audio_sample_rate": 22050,
"base_url": "s3://bluepi-encoded.s3.amazonaws.com/",
"decoder_bitrate_cap": 300,
"decoder_buffer_size": 800,
"filename": "sample-240k.m3u8",
"max_frame_rate": 15,
"public": 1,
"type": "segmented",
"video_bitrate": 200,
"width": 400,
"format": "ts"
},
{
"audio_bitrate": 56,
"audio_sample_rate": 22050,
"base_url": "s3://bluepi-encoded.s3.amazonaws.com/",
"decoder_bitrate_cap": 600,
"decoder_buffer_size": 1600,
"filename": "sample-440k.m3u8",
"public": 1,
"type": "segmented",
"video_bitrate": 400,
"width": 400,
"format": "ts"
},
{
"audio_bitrate": 56,
"audio_sample_rate": 22050,
"base_url": "s3://bluepi-encoded.s3.amazonaws.com/",
"decoder_bitrate_cap": 900,
"decoder_buffer_size": 2400,
"filename": "sample-640k.m3u8",
"public": 1,
"type": "segmented",
"video_bitrate": 600,
"width": 480,
"format": "ts"
},
{
"audio_bitrate": 56,
"audio_sample_rate": 22050,
"base_url": "s3://bluepi-encoded.s3.amazonaws.com/",
"decoder_bitrate_cap": 1500,
"decoder_buffer_size": 4000,
"filename": "sample-1040k.m3u8",
"public": 1,
"type": "segmented",
"video_bitrate": 1000,
"width": 640,
"format": "ts"
},
{
"audio_bitrate": 56,
"audio_sample_rate": 22050,
"base_url": "s3://bluepi-encoded.s3.amazonaws.com/",
"decoder_bitrate_cap": 2250,
"decoder_buffer_size": 6000,
"filename": "sample-1540k.m3u8",
"public": 1,
"type": "segmented",
"video_bitrate": 1500,
"width": 960,
"format": "ts"
},
{
"audio_bitrate": 56,
"audio_sample_rate": 22050,
"base_url": "s3://bluepi-encoded.s3.amazonaws.com/",
"decoder_bitrate_cap": 3000,
"decoder_buffer_size": 8000,
"filename": "sample-2040k.m3u8",
"public": 1,
"type": "segmented",
"video_bitrate": 2000,
"width": 1024,
"format": "ts"
},
{
"base_url": "s3://bluepi-encoded.s3.amazonaws.com/",
"filename": "playlist.m3u8",
"public": 1,
"streams": [
{
"bandwidth": 2040,
"path": "sample-2040k.m3u8"
},
{
"bandwidth": 1540,
"path": "sample-1540k.m3u8"
},
{
"bandwidth": 1040,
"path": "sample-1040k.m3u8"
},
{
"bandwidth": 640,
"path": "sample-640k.m3u8"
},
{
"bandwidth": 440,
"path": "sample-440k.m3u8"
},
{
"bandwidth": 240,
"path": "sample-240k.m3u8"
},
{
"bandwidth": 64,
"path": "sample-64k.m3u8"
}
],
"type": "playlist"
}
]
}

Creating MP4 and HLS Outputs Together

Generally you don’t need only HLS encoding, because some flash players support RTMP protocol which is secured and with the help of CDN it transfer data in segments so you need MP4 files too. For that purpose you have to create/encode your original videos in MP4 as well as HLS outputs, so in this section we will discuss how you can generate both types of outputs together in one request.
With Zencoder you can encode your videos to MP4 and then also repackage those as HLS outputs, through a process we call “transmuxing” (rather than transcoding). Transmuxing will repackage existing MP4 videos into the MPEG TS segments necessary for HTTP Live Streaming (HLS), without having to re-encode the video files. Using dependent outputs you can create your H.264 files plus the segmented files all in a single job, producing faster turnaround at lower cost; transmuxed outputs are charged at 1/4 of the cost of encoding. The resulting job creates 6 outputs – 2 charged at your account’s regular rate, 3 transmuxed files at 1/4 of the encoding cost, and 1 playlist, which is free.
For this, you’ll create a single job with two main sets of outputs (plus the playlists). The first set of outputs will be normal H.264/AAC MP4 files with a few special settings to allow them to work as source files for HLS outputs. The second set of outputs will use those MP4 files as “source” inputs, transmuxing their content to HLS outputs. Since these are dependent outputs, they will wait until their corresponding source finishes before being scheduled to process.
The key part of the process is using source, a new option in V2 of the Zencoder API. Source tells an output to use the file created by another output on the job for processing, instead of the input file. In this situation, Zencoder will create H.264 files based on the input file at requested bitrates. As each of those outputs finishes, a segmented version will then be created from the output.
We have already discussed normal encoding to MP4 output in  last blog and HLS encoding as explained above, so the merged request will look something like as follows:


{
"input": "s3://bluepi-original.s3.amazonaws.com/sample.mov",
"outputs": [
{
"label": "high",
"url": "s3://bluepi-encoded.s3.amazonaws.com/high.mp4",
"audio_bitrate": 160,
"audio_sample_rate": 48000,
"height": 720,
"width": 1280,
"max_frame_rate": 30,
"video_bitrate": 5000,
"h264_level": "3.1",
"format": "mp4",
"decoder_bitrate_cap": 3000,
"decoder_buffer_size": 8000,
"h264_reference_frames": 1,
"h264_profile": "main",
"forced_keyframe_rate": "0.1",
"decimate": 2,
"watermarks": [
{
"x": "95%",
"y": "-92%",
"url": "s3://bluepi-images.s3.amazonaws.com/videos/logo-watermark-desktop.png"
}
],
"public": false
},
{
"source": "high",
"label": "hls-high",
"type": "segmented",
"format": "ts",
"copy_audio": "true",
"copy_video": "true",
"url": "s3://bluepi-encoded.s3.amazonaws.com/hls-high/hls-high.m3u8",
"public": false
},
{
"label": "main",
"url": "s3://bluepi-encoded.s3.amazonaws.com/main.mp4",
"audio_bitrate": 128,
"audio_sample_rate": 44100,
"height": 480,
"width": 640,
"max_frame_rate": 30,
"video_bitrate": 2000,
"h264_level": "3",
"format": "mp4",
"decoder_bitrate_cap": 2250,
"decoder_buffer_size": 6000,
"h264_reference_frames": 1,
"h264_profile": "main",
"forced_keyframe_rate": "0.1",
"decimate": 2,
"watermarks": [
{
"x": "95%",
"y": "-92%",
"url": "s3://bluepi-images.s3.amazonaws.com/videos/logo-watermark-desktop.png"
}
],
"public": false
},
{
"source": "main",
"label": "hls-main",
"type": "segmented",
"format": "ts",
"copy_audio": "true",
"copy_video": "true",
"url": "s3://bluepi-encoded.s3.amazonaws.com/hls-main/hls-main.m3u8",
"public": false
},
{
"label": "baseline",
"url": "s3://bluepi-encoded.s3.amazonaws.com/baseline.mp4",
"audio_bitrate": 128,
"audio_sample_rate": 44100,
"height": 320,
"width": 480,
"max_frame_rate": 30,
"video_bitrate": 1500,
"h264_level": "3.1",
"format": "mp4",
"decoder_bitrate_cap": 1500,
"decoder_buffer_size": 4000,
"h264_reference_frames": 1,
"h264_profile": "main",
"forced_keyframe_rate": "0.1",
"decimate": 2,
"watermarks": [
{
"x": "92%",
"y": "-88%",
"url": "s3://bluepi-images.s3.amazonaws.com/videos/logo-watermark-desktop.png"
}
],
"public": false
},
{
"source": "baseline",
"label": "hls-baseline",
"type": "segmented",
"format": "ts",
"copy_audio": "true",
"copy_video": "true",
"url": "s3://bluepi-encoded.s3.amazonaws.com/hls-baseline/hls-baseline.m3u8",
"public": false
},
{
"label": "baselinelow",
"url": "s3://bluepi-encoded.s3.amazonaws.com/baselinelow.mp4",
"audio_bitrate": 128,
"audio_sample_rate": 44100,
"height": 240,
"width": 320,
"max_frame_rate": 30,
"video_bitrate": 768,
"h264_level": "1.3",
"format": "mp4",
"decoder_bitrate_cap": 600,
"decoder_buffer_size": 1600,
"h264_reference_frames": 1,
"h264_profile": "main",
"forced_keyframe_rate": "0.1",
"decimate": 2,
"watermarks": [
{
"x": "92%",
"y": "-88%",
"url": "s3://bluepi-images.s3.amazonaws.com/videos/logo-watermark.png"
}
],
"public": false
},
{
"source": "baselinelow",
"label": "hls-baselinelow",
"type": "segmented",
"format": "ts",
"copy_audio": "true",
"copy_video": "true",
"url": "s3://bluepi-encoded.s3.amazonaws.com/hls-baselinelow/hls-baselinelow.m3u8",
"public": false
},
{
"label": "3gp",
"url": "s3://bluepi-encoded.s3.amazonaws.com/3gp.mp4",
"audio_bitrate": 24,
"audio_sample_rate": 16000,
"height": 240,
"width": 320,
"max_frame_rate": 15,
"video_bitrate": 192,
"h264_level": null,
"format": "mp4",
"decoder_bitrate_cap": 300,
"decoder_buffer_size": 1200,
"h264_reference_frames": 1,
"h264_profile": "main",
"forced_keyframe_rate": "0.1",
"decimate": 2,
"watermarks": [
{
"x": "93%",
"y": "-90%",
"url": "s3://bluepi-images.s3.amazonaws.com/videos/logo-watermark-mobile.png"
}
],
"public": false
},
{
"source": "3gp",
"label": "hls-3gp",
"type": "segmented",
"format": "ts",
"copy_audio": "true",
"copy_video": "true",
"url": "s3://bluepi-encoded.s3.amazonaws.com/hls-3gp/hls-3gp.m3u8",
"public": false
},
{
"source": "baselinelow",
"label": "hls-audio-only",
"type": "segmented",
"format": "aac",
"segment_video_snapshots": "true",
"url": "s3://bluepi-encoded.s3.amazonaws.com/hls-audio-only/hls-audio-only.m3u8",
"copy_audio": "true",
"skip_video": "true",
"public": false
},
{
"type": "playlist",
"url": "s3://bluepi-encoded.s3.amazonaws.com/playlist.m3u8",
"streams": [
{
"path": "hls-high/hls-high.m3u8",
"bandwidth": 2040
},
{
"path": "hls-main/hls-main.m3u8",
"bandwidth": 1540
},
{
"path": "hls-baseline/hls-baseline.m3u8",
"bandwidth": 640
},
{
"path": "hls-baselinelow/hls-baselinelow.m3u8",
"bandwidth": 240
},
{
"path": "hls-3gp/hls-3gp.m3u8",
"bandwidth": 64
},
{
"path": "hls-audio-only/hls-audio-only.m3u8",
"bandwidth": 56
}
],
"public": false
}
]
}

Protecting Videos on Devices with HLS Encryption

Zencoder can perform AES encryption of video files. Our implementation is designed to be compatible with HTTP Live Streaming (HLS) outputs, but can be used to encrypt any video file similarly. When used for segmented outputs, only the segment files will be encrypted (leaving the manifest/playlist files readable, as expected). For HLS outputs, the necessary key/IV data will be added to the M3U8 manifest file, and encryption key files will be included with the other deliverable files if applicable.
Encryption is turned on when encryption_key, encryption_key_url, encryption_method, or encryption_key_rotation_period are specified.

Setting Default Description
encryption_method none Set the encryption method to use for encrypting.
encryption_key none Set a single encryption key to use rather than having Zencoder generate one
encryption_key_url none Set a URL to a single encryption key to use rather than having Zencoder generate one
encryption_key_rotation_period none Rotate to a new encryption key after a number of segments
encryption_key_url_prefix none Prepend key URLs with the passed string
encryption_iv none Set an initialization vector to use when encrypting
encryption_password none Sets a password to use for generating an initialization vector

The Whole picture

Now let’s see whole picture with Mp4 and HLS encoding and with Encryption.


{
"input": "s3://bluepi-original.s3.amazonaws.com/sample.mov",
"outputs": [
{
"label": "high",
"url": "s3://bluepi-encoded.s3.amazonaws.com/high.mp4",
"audio_bitrate": 160,
"audio_sample_rate": 48000,
"height": 720,
"width": 1280,
"max_frame_rate": 30,
"video_bitrate": 5000,
"h264_level": "3.1",
"format": "mp4",
"decoder_bitrate_cap": 3000,
"decoder_buffer_size": 8000,
"h264_reference_frames": 1,
"h264_profile": "main",
"forced_keyframe_rate": "0.1",
"decimate": 2,
"watermarks": [
{
"x": "95%",
"y": "-92%",
"url": "s3://bluepi-images.s3.amazonaws.com/videos/logo-watermark-desktop.png"
}
],
"public": false
},
{
"source": "high",
"label": "hls-high",
"type": "segmented",
"format": "ts",
"copy_audio": "true",
"copy_video": "true",
"url": "s3://bluepi-encoded.s3.amazonaws.com/hls-high/hls-high.m3u8",
"public": false,
"encryption_method": "aes-128",
"encryption_key": "a5xxxxxxxxxx63"
},
{
"label": "main",
"url": "s3://bluepi-encoded.s3.amazonaws.com/main.mp4",
"audio_bitrate": 128,
"audio_sample_rate": 44100,
"height": 480,
"width": 640,
"max_frame_rate": 30,
"video_bitrate": 2000,
"h264_level": "3",
"format": "mp4",
"decoder_bitrate_cap": 2250,
"decoder_buffer_size": 6000,
"h264_reference_frames": 1,
"h264_profile": "main",
"forced_keyframe_rate": "0.1",
"decimate": 2,
"watermarks": [
{
"x": "95%",
"y": "-92%",
"url": "s3://bluepi-images.s3.amazonaws.com/videos/logo-watermark-desktop.png"
}
],
"public": false
},
{
"source": "main",
"label": "hls-main",
"type": "segmented",
"format": "ts",
"copy_audio": "true",
"copy_video": "true",
"url": "s3://bluepi-encoded.s3.amazonaws.com/hls-main/hls-main.m3u8",
"public": false,
"encryption_method": "aes-128",
"encryption_key": "a5xxxxxxxxxx63"
},
{
"label": "baseline",
"url": "s3://bluepi-encoded.s3.amazonaws.com/baseline.mp4",
"audio_bitrate": 128,
"audio_sample_rate": 44100,
"height": 320,
"width": 480,
"max_frame_rate": 30,
"video_bitrate": 1500,
"h264_level": "3.1",
"format": "mp4",
"decoder_bitrate_cap": 1500,
"decoder_buffer_size": 4000,
"h264_reference_frames": 1,
"h264_profile": "main",
"forced_keyframe_rate": "0.1",
"decimate": 2,
"watermarks": [
{
"x": "92%",
"y": "-88%",
"url": "s3://bluepi-images.s3.amazonaws.com/videos/logo-watermark-desktop.png"
}
],
"public": false
},
{
"source": "baseline",
"label": "hls-baseline",
"type": "segmented",
"format": "ts",
"copy_audio": "true",
"copy_video": "true",
"url": "s3://bluepi-encoded.s3.amazonaws.com/hls-baseline/hls-baseline.m3u8",
"public": false,
"encryption_method": "aes-128",
"encryption_key": "a5xxxxxxxxxx63"
},
{
"label": "baselinelow",
"url": "s3://bluepi-encoded.s3.amazonaws.com/baselinelow.mp4",
"audio_bitrate": 128,
"audio_sample_rate": 44100,
"height": 240,
"width": 320,
"max_frame_rate": 30,
"video_bitrate": 768,
"h264_level": "1.3",
"format": "mp4",
"decoder_bitrate_cap": 600,
"decoder_buffer_size": 1600,
"h264_reference_frames": 1,
"h264_profile": "main",
"forced_keyframe_rate": "0.1",
"decimate": 2,
"watermarks": [
{
"x": "92%",
"y": "-88%",
"url": "s3://bluepi-images.s3.amazonaws.com/videos/logo-watermark.png"
}
],
"public": false
},
{
"source": "baselinelow",
"label": "hls-baselinelow",
"type": "segmented",
"format": "ts",
"copy_audio": "true",
"copy_video": "true",
"url": "s3://bluepi-encoded.s3.amazonaws.com/hls-baselinelow/hls-baselinelow.m3u8",
"public": false,
"encryption_method": "aes-128",
"encryption_key": "a5xxxxxxxxxx63"
},
{
"label": "3gp",
"url": "s3://bluepi-encoded.s3.amazonaws.com/3gp.mp4",
"audio_bitrate": 24,
"audio_sample_rate": 16000,
"height": 240,
"width": 320,
"max_frame_rate": 15,
"video_bitrate": 192,
"h264_level": null,
"format": "mp4",
"decoder_bitrate_cap": 300,
"decoder_buffer_size": 1200,
"h264_reference_frames": 1,
"h264_profile": "main",
"forced_keyframe_rate": "0.1",
"decimate": 2,
"watermarks": [
{
"x": "93%",
"y": "-90%",
"url": "s3://bluepi-images.s3.amazonaws.com/videos/logo-watermark-mobile.png"
}
],
"public": false
},
{
"source": "3gp",
"label": "hls-3gp",
"type": "segmented",
"format": "ts",
"copy_audio": "true",
"copy_video": "true",
"url": "s3://bluepi-encoded.s3.amazonaws.com/hls-3gp/hls-3gp.m3u8",
"public": false,
"encryption_method": "aes-128",
"encryption_key": "a5xxxxxxxxxx63"
},
{
"source": "baselinelow",
"label": "hls-audio-only",
"type": "segmented",
"format": "aac",
"segment_video_snapshots": "true",
"url": "s3://bluepi-encoded.s3.amazonaws.com/hls-audio-only/hls-audio-only.m3u8",
"copy_audio": "true",
"skip_video": "true",
"public": false,
"encryption_method": "aes-128",
"encryption_key": "a5xxxxxxxxxx63"
},
{
"type": "playlist",
"url": "s3://bluepi-encoded.s3.amazonaws.com/playlist.m3u8",
"streams": [
{
"path": "hls-high/hls-high.m3u8",
"bandwidth": 2040
},
{
"path": "hls-main/hls-main.m3u8",
"bandwidth": 1540
},
{
"path": "hls-baseline/hls-baseline.m3u8",
"bandwidth": 640
},
{
"path": "hls-baselinelow/hls-baselinelow.m3u8",
"bandwidth": 240
},
{
"path": "hls-3gp/hls-3gp.m3u8",
"bandwidth": 64
},
{
"path": "hls-audio-only/hls-audio-only.m3u8",
"bandwidth": 56
}
],
"public": false
}
]
}

References

By Barkat Dhillon

TAGS :

Invest in better
solutions today

Contact Us