Objective
Provide code snippets to integrate Per-Title encoding with Multi-DRM. This stems from the FAQ question - Can I use Per-Title encodings with DRM solutions like Widevine, PlayReady, or Fairplay?
This post assumes you’re familiar with
When integrating Per Title w/ DRM the two key things to consider:
Option 1
- If you use the autoRepresentations feature, make sure to have one Per-Title template Stream with configured resolution close to each category where you want to use a different encryption key. E.g., for SD use a width of 640.
In essence we add the SD/HD keys to the resolution that will be picked by adoptConfigurationThreshold
configuration.
- Modify per_title_encoding.py per the following
def main():
...
# Define keys per the resolution
drm_keys_resolution_map = [
{ # SD Key; PerTitle Renditions w/ Resolution closer to 640p will use SD key
"height": 640, "key": "B01F8C032F5564B6C81DCD340F480978", "keyId": "E920E5426157291C177F8BBD34026696"
},
{
# HD Key; PerTitle Renditions w/ Resolution closer to 720p will use HD key
"height": 720, "key": "770A8A65DA156D24EE2A093277530142", "keyId": "ABBA271E8BCF552BBD2E86A434A9A5D9"
}]
for kvp in drm_keys_resolution_map:
# Add an H.264 video stream to the encoding
h264_video_configuration = _create_h264_video_configuration(
height=kvp.get("resolution"))
h264_video_stream = _create_stream(
encoding=encoding,
encoding_input=http_input,
input_path=input_file_path,
codec_configuration=h264_video_configuration,
stream_mode=StreamMode.PER_TITLE_TEMPLATE
)
# Create a fragmented MP4 muxing with the H.264 stream
video_muxing = _create_fmp4_muxing(
encoding=encoding,
stream=h264_video_stream
)
# Create DRM config for fragmented MP4 muxing
_create_drm_config(
encoding=encoding,
muxing=video_muxing,
output=output,
output_path="video/{height}/{bitrate}_{uuid}",
key=kvp.get("key"),
keyId=kvp.get("keyId")
)
...
- Include the helper code
Option 2
If you are not using autoRepresentations, just configure the DRM configuration on every Per-Title template stream with the encryption key you want to use for that representation.
More straightforward as you specify the key for each resolution/rendition in code.
- Modify per_title_encoding.py per the following
def main():
...
# Define the resolutions/renditions you want
resolutions = [480, 640, 720, 1080]
# Define keys per resolution bounds
drm_keys_resolution_map = [
{ # SD Key; PerTitle Renditions w/ height <720p will use SD key
"height": 720, "key": "B01F8C032F5564B6C81DCD340F480978", "keyId": "E920E5426157291C177F8BBD34026696"
},
{
# HD Key; PerTitle Renditions w/ height <3840p will use HD key
"height": 3840, "key": "770A8A65DA156D24EE2A093277530142", "keyId": "ABBA271E8BCF552BBD2E86A434A9A5D9"
}]
for height in resolutions:
# Add an H.264 video stream to the encoding
kvp = None
for each in drm_keys_resolution_map:
if height < each.get('height'):
kvp = each
break
h264_video_configuration = _create_h264_video_configuration(height=height)
h264_video_stream = _create_stream(
encoding=encoding,
encoding_input=http_input,
input_path=input_file_path,
codec_configuration=h264_video_configuration,
stream_mode=StreamMode.PER_TITLE_TEMPLATE
)
# Create a fragmented MP4 muxing with the H.264 stream
video_muxing = _create_fmp4_muxing(
encoding=encoding,
stream=h264_video_stream
)
# Create DRM config for fragmented MP4 muxing
_create_drm_config(
encoding=encoding,
muxing=video_muxing,
output=output,
output_path="video/{height}/{bitrate}_{uuid}",
key=kvp.get("key"),
keyId=kvp.get("keyId")
)
...
- Include the helper code
Helper Code
# UPDATE imports
from bitmovin_api_sdk import ... , \
CencDrm, CencFairPlay, CencWidevine, CencPlayReady
# UPDATE method _create_h264_video_configuration
def _create_h264_video_configuration(height):
...
config = H264VideoConfiguration(
name="Base H.264 video config",
preset_configuration=PresetConfiguration.VOD_STANDARD,
height=height
)
...
# UPDATE method _create_fmp4_muxing
def _create_fmp4_muxing(encoding, output=None, output_path=None, stream=None):
...
# If no output is specified don't create the (clear) one; we'll create the output in the `_create_drm_config`
outputs = None
if output != None:
outputs = [_build_encoding_output(
output=output, output_path=output_path)]
muxing = Fmp4Muxing(
segment_length=4.0,
outputs=outputs,
streams=[MuxingStream(stream_id=stream.id)]
)
...
# ADD method _create_drm_config
def _create_drm_config(encoding, muxing, output, output_path, key, keyId):
# type: (Encoding, Muxing, Output, str) -> CencDrm
"""
Adds an MPEG-CENC DRM configuration to the muxing to encrypt its output. Widevine and FairPlay
specific fields will be included into DASH and HLS manifests to enable key retrieval using
either DRM method.
<p>API endpoint:
https://bitmovin.com/docs/encoding/api-reference/sections/encodings#/Encoding/PostEncodingEncodingsMuxingsFmp4DrmCencByEncodingIdAndMuxingId
:param encoding:
:param muxing:
:param output:
:param output_path:
:return:
"""
widevine_drm = CencWidevine(
pssh=config_provider.get_drm_widevine_pssh()
)
cenc_fair_play = CencFairPlay(
iv=config_provider.get_drm_fairplay_iv(),
uri=config_provider.get_drm_fairplay_uri()
)
cenc_drm = CencDrm(
outputs=[_build_encoding_output(
output=output, output_path=output_path)],
key=key,
kid=keyId,
widevine=widevine_drm,
fair_play=cenc_fair_play
)
return bitmovin_api.encoding.encodings.muxings.fmp4.drm.cenc.create(encoding_id=encoding.id,
muxing_id=muxing.id,
cenc_drm=cenc_drm)