Code Snippets to integrate Per-Title encodings with DRM

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

  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.

  1. 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")
        )
    ...
  1. 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.

  1. 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")
        )
    ...
  1. 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)
2 Likes