shaka-project / shaka-packager

A media packaging and development framework for VOD and Live DASH and HLS applications, supporting Common Encryption for Widevine and other DRM Systems.
https://shaka-project.github.io/shaka-packager/
Other
1.95k stars 502 forks source link

DASH: Indicate license acquisition url in mpd for clearkey content. #1197

Open sr1990 opened 1 year ago

sr1990 commented 1 year ago

As per https://dashif.org/guidelines/iop-v5/#part-6-content-protection-and-security - 8 Use of W3C Clear Key with DASH,

A DRM system specific ContentProtection descriptor for Clear Key shall use the systemID=e2719d58-a985-
b3c9-781a-b030af78d30e and the attribute @value shall be equal to "ClearKey1.0".
The Laurl element shall be used to indicate the license server URL. The attribute @licenseType describes the type
of the license served by this license server.

Example: MPD url: https://media.axprod.net/TestVectors/v7-MultiDRM-SingleKey/Manifest_1080p_ClearKey.mpd

<ContentProtection schemeIdUri="urn:mpeg:dash:mp4protection:2011" value="cenc" cenc:default_KID="9eb4050d-e44b-4802-932e-27d75083e266" />
 <ContentProtection value="ClearKey1.0" schemeIdUri="urn:uuid:e2719d58-a985-b3c9-781a-b030af78d30e">
    <clearkey:Laurl Lic_type="EME-1.0">https://drm-clearkey-testvectors.axtest.net/AcquireLicense</clearkey:Laurl>
</ContentProtection> 

When player encounters the above ContentProtection elements,it will

  1. parse the default_KID from ContentProtection in the manifest and construct a PSSH box from it to feed to the ClearKey CDM.
  2. parse license url and send a POST request to license url as per https://w3c.github.io/encrypted-media/#clear-key-request-format to retrieve the decryption key/s.

This is already supported by shaka player, dash-if player and Android's Exoplayer.

Currently,

../packager \
  'in=Sintel-1280x720.mp4,stream=video,init_segment=h264_1280p/init.mp4,segment_template=h264_1280p/$Number$.m4s' \
  'in=Sintel-audio.mp4,stream=audio,init_segment=audioSintel/init.mp4,segment_template=audioSintel/$Number$.m4s' \
  --mpd_output cencExample1.mpd --generate_static_live_mpd \
  --enable_raw_key_encryption --enable_raw_key_decryption --clear_lead=0 \
  -key 2844832a5bfe06350dfd95caff1b6c5c -key_id 444c849e58101486ec19ceb33ec00287

will add the following to mpd:

      <ContentProtection schemeIdUri="urn:uuid:1077efec-c0b2-4d02-ace3-3c1e52e2fb4b">
        <cenc:pssh>AAAANHBzc2gBAAAAEHfv7MCyTQKs4zweUuL7SwAAAAFETISeWBAUhuwZzrM+wAKHAAAAAA==</cenc:pssh>
      </ContentProtection>

There is no way to indicate license acquisition url.

One possible solution to indicate a licence server of @licenseType EME-1.0 is to add a flag like --clear_key_license_url "https://drm-clearkey-testvectors.axtest.net/AcquireLicense".

Example:

../packager \
  'in=Sintel-1280x720.mp4,stream=video,init_segment=h264_1280p/init.mp4,segment_template=h264_1280p/$Number$.m4s' \
  'in=Sintel-audio.mp4,stream=audio,init_segment=audioSintel/init.mp4,segment_template=audioSintel/$Number$.m4s' \
  --mpd_output cencExample1.mpd --generate_static_live_mpd \
  --enable_raw_key_encryption --enable_raw_key_decryption --clear_lead=0 \
  -key 2844832a5bfe06350dfd95caff1b6c5c -key_id 444c849e58101486ec19ceb33ec00287 \
   --clear_key_license_url "https://drm-clearkey-testvectors.axtest.net/AcquireLicense"

when clear_key_license_url is present, ContentProtection element with schemeIdUri="urn:uuid:1077efec-c0b2-4d02-ace3-3c1e52e2fb4b" should be skipped and <ContentProtection value="ClearKey1.0" schemeIdUri="urn:uuid:e2719d58-a985-b3c9-781a-b030af78d30e">should be added.

@joeyparrish please let me know what you think.

InSantoshMahto commented 4 months ago

Hi @sr1990 i am also exploring same license server option

cosmin commented 4 months ago

@sr1990 that sounds like a reasonable approach, are you planning to implement this?

sr1990 commented 4 months ago

Hey @cosmin, for the above approach I can think of the following steps:

  1. Add flag after https://github.com/shaka-project/shaka-packager/blob/main/packager/mpd/base/xml/xml_node.cc#L36
    ABSL_FLAG(std::string,
          clear_key_license_url,
          "",
          "Clear key license url.");

Declare the flag after https://github.com/shaka-project/shaka-packager/blob/84009d82ef1b75ec6dab55e8dc5c888d845a956c/packager/mpd/base/xml/xml_node_unittest.cc#L24

ABSL_DECLARE_FLAG(std::string, clear_key_license_url);
  1. In RepresentationBaseXmlNode::AddContentProtectionElement, if content protection element's scheme_id_uri == "urn:uuid:1077efec-c0b2-4d02-ace3-3c1e52e2fb4b" and clear key license url is available, substitute the raw key's ContentProtection element with clearkey's ContentProtection element. Example:

    if (content_protection_element.scheme_id_uri == "urn:uuid:1077efec-c0b2-4d02-ace3-3c1e52e2fb4b" &&
    !absl::GetFlag(FLAGS_clear_key_license_url).empty()) {
    
      // Create ContentProtectionElement
      ContentProtectionElement temp1;
      temp1.value = "ClearKey1.0";
      temp1.scheme_id_uri = "urn:uuid:e2719d58-a985-b3c9-781a-b030af78d30e";
    
      // Create element Clearkey
      Element clearkey;
      clearkey.name = "clearkey:Laurl";
      clearkey.content = absl::GetFlag(FLAGS_clear_key_license_url);
      clearkey.attributes["Lic_type"] = "EME-1.0";
      // Push element into contentProtectionElement
      temp1.subelements.push_back(clearkey);
    
      // Create ContentProtection node.
      XmlNode content_protection_temp("ContentProtection");
    // @value is an optional attribute.
    if (!temp1.value.empty()) {
            RCHECK(content_protection_temp.SetStringAttribute(
                "value", temp1.value));
    }
    RCHECK(content_protection_temp.SetStringAttribute(
            "schemeIdUri", temp1.scheme_id_uri));
    
    RCHECK(content_protection_temp.AddElements(
          temp1.subelements));
    
      return AddChild(std::move(content_protection_temp));
    }  

    Also a better approach would be to create ClearKeyKeySource similar to WidevineKeySource and add a flag to enable clearkey encryption --enable_clear_key_encryption

When the packager parameters are parsed at https://github.com/shaka-project/shaka-packager/blob/84009d82ef1b75ec6dab55e8dc5c888d845a956c/packager/app/packager_main.cc#L365 in packager_main.cc::GetPackagingParams(), set the key provider to Clearkey. and while creating encryption key source at https://github.com/shaka-project/shaka-packager/blob/84009d82ef1b75ec6dab55e8dc5c888d845a956c/packager/packager.cc#L832 in Packager::Initialize, create and fetch keys using ClearKeyKeySource. For fetching the keys, ClearKeyKeySource needs to send the POST request for each/all variant to the clear key license server as per https://w3c.github.io/encrypted-media/#clear-key-request-format.

I can think of the following scenarios:

  1. Keyid and key same for all the variants - No need to fetch keys from clearkey license server

    packager \
    'in=Sintel-1280x720.mp4,stream=video,init_segment=h264_1280p/init.mp4,segment_template=h264_1280p/$Number$.m4s' \
    'in=Sintel-768x432.mp4,stream=video,init_segment=h264_768p/init.mp4,segment_template=h264_768p/$Number$.m4s' \
    'in=Sintel-audio.mp4,stream=audio,init_segment=audioSintel/init.mp4,segment_template=audioSintel/$Number$.m4s' \
    --mpd_output h264SintelRaw.mpd --generate_static_live_mpd \
    --enable_clear_key_encryption --clear_lead=0 \
    -key 2844832a5bfe06350dfd95caff1b6c5c -key_id 444c849e58101486ec19ceb33ec00287 \
    --clear_key_license_url "http://xyz/AcquireLicense"
  2. Keyid given and key needs to be fetched from the clearkey license url server

    packager \
    'in=Sintel-1280x720.mp4,stream=video,init_segment=h264_1280p/init.mp4,segment_template=h264_1280p/$Number$.m4s' \
    'in=Sintel-768x432.mp4,stream=video,init_segment=h264_768p/init.mp4,segment_template=h264_768p/$Number$.m4s' \
    'in=Sintel-audio.mp4,stream=audio,init_segment=audioSintel/init.mp4,segment_template=audioSintel/$Number$.m4s' \
    --mpd_output h264SintelRaw.mpd --generate_static_live_mpd \
    --enable_clear_key_encryption --clear_lead=0 \
    -key_id 444c849e58101486ec19ceb33ec00287 \
    --clear_key_license_url "http://xyz/AcquireLicense"
  3. Using drm_label where key_id and key are given: no need to fetch keys from clearkey license server.

    $ packager \
    in=h264_baseline_360p_600.mp4,stream=audio,output=audio.mp4,drm_label=AUDIO \
    in=h264_baseline_360p_600.mp4,stream=video,output=h264_360p.mp4,drm_label=SD \
    in=h264_main_480p_1000.mp4,stream=video,output=h264_480p.mp4,drm_label=SD \
    in=h264_main_720p_3000.mp4,stream=video,output=h264_720p.mp4,drm_label=HD \
    in=h264_high_1080p_6000.mp4,stream=video,output=h264_1080p.mp4,drm_label=HD \
    --enable_clear_key_encryption --clear_lead=0 \
    --keys label=AUDIO:key_id=f3c5e0361e6654b28f8049c778b23946:key=a4631a153a443df9eed0593043db7519,label=SD:key_id=abba271e8bcf552bbd2e86a434a9a5d9:key=69eaa802a6763af979e8d1940fb88392,label=HD:key_id=6d76f25cb17f5e16b8eaef6bbf582d8e:key=cb541084c99731aef4fff74500c12ead \
    --clear_key_license_url "http://xyz/AcquireLicense"\
    --mpd_output h264.mpd
  4. Using drm_label where keyid is given and key needs to fetched from licence server using ClearKeySource

    $ packager \
    in=h264_baseline_360p_600.mp4,stream=audio,output=audio.mp4,drm_label=AUDIO \
    in=h264_baseline_360p_600.mp4,stream=video,output=h264_360p.mp4,drm_label=SD \
    in=h264_main_480p_1000.mp4,stream=video,output=h264_480p.mp4,drm_label=SD \
    in=h264_main_720p_3000.mp4,stream=video,output=h264_720p.mp4,drm_label=HD \
    in=h264_high_1080p_6000.mp4,stream=video,output=h264_1080p.mp4,drm_label=HD \
    --enable_clear_key_encryption \
    --keys label=AUDIO:key_id=f3c5e0361e6654b28f8049c778b23946,label=SD:key_id=abba271e8bcf552bbd2e86a434a9a5d9,label=HD:key_id=6d76f25cb17f5e16b8eaef6bbf582d8e \
    --clear_key_license_url "http://xyz/AcquireLicense"\
    --mpd_output h264.mpd

We can start with the first approach for users who prefer avoiding the packager's license server request overhead. It's simpler to implement – just substitute the ContentProtection element. I think we may require some time to implement the second approach. However, I'm available to review and test whenever necessary, if someone else takes on the implementation. Please let me know what you think.