canalplus / rx-player

DASH/Smooth HTML5 Video Player
https://developers.canal-plus.com/rx-player/
Apache License 2.0
847 stars 125 forks source link

Many DRM session created #1183

Open el-gringo opened 1 year ago

el-gringo commented 1 year ago

Hello,

Since release 3.27.0, the player creates many temporary DRM sessions, triggering many calls to our license server.

It seems that the current algorithm creates a new DRM session for each keyId instead of re-using an already created session. We are using DASH + Widevine.

Here are the logs of a DRM session :

DRM: Clearing-up DRM session.
DRM: Nothing to clear. Returning right away. No state = true
DRM: Searching for compatible MediaKeySystemAccess
DRM: Found compatible keysystem com.widevine.alpha 1
DRM: Calling createMediaKeys on the MediaKeySystemAccess
DRM: MediaKeys created with success
DRM: Attaching MediaKeys to the media element
DRM: MediaKeys attached with success
DRM: Creating a new temporary session
DRM: Binding session events 
DRM: Creating a new temporary session
DRM: Binding session events 
DRM: Creating a new temporary session
DRM: Binding session events 
DRM: Received message event, type license-request 9F5D37EFCFD88814BA1D701CC7345AC7
DRM: Received message event, type license-request 1E6586EA9642D5909B05DA17C40A9677
DRM: Received message event, type license-request A73E03F09FC93FA1FA2537BEBC3EA6AF
DRM: Updating MediaKeySession with message
DRM: MediaKeySession update succeeded.
DRM: keystatuseschange event received 9F5D37EFCFD88814BA1D701CC7345AC7

The kind of manifest we use :

<?xml version="1.0" encoding="UTF-8"?>
<MPD xmlns="urn:mpeg:dash:schema:mpd:2011" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="urn:mpeg:dash:schema:mpd:2011 DASH-MPD.xsd" xmlns:cenc="urn:mpeg:cenc:2013" profiles="urn:mpeg:dash:profile:isoff-live:2011" type="dynamic" publishTime="2022-11-22T15:40:13Z" availabilityStartTime="1970-01-01T00:00:00Z" minimumUpdatePeriod="PT2S" minBufferTime="PT6.4S" timeShiftBufferDepth="PT14400S" suggestedPresentationDelay="PT9.6S">
  <Period id="0" start="PT0S">
    <AdaptationSet id="0" group="1" segmentAlignment="true" startWithSAP="1" contentType="video">
      <ContentProtection schemeIdUri="urn:uuid:edef8ba9-79d6-4ace-a3c8-27dcd51d21ed"><cenc:pssh>AAAAiHBzc2gAAAAA7e+LqXnWSs6jyCfc1R0h7QAAAGgIARIQH7rsOV0BM3TmyyBxB90bChIQx49W2DcwfMI0bsq178WDyBIQTPw+QqeiYd3hKULfDtNe5BIQEqP6hNUWi6TrJMz14RSXARIQWA0ccDTY6BgjdP53xHdJoBoAKgAyADgASABQAA==</cenc:pssh></ContentProtection>      
      <ContentProtection schemeIdUri="urn:mpeg:dash:mp4protection:2011" value="cenc" cenc:default_KID="4cfc3e42-a7a2-61dd-e129-42df0ed35ee4"/>
      <Representation id="379" bandwidth="400000" codecs="avc1.64000d" mimeType="video/mp4" width="384" height="216" frameRate="25">
        <SegmentTemplate timescale="90000" initialization="0_1_379_init" media="0_1_379_$Time$">
          <SegmentTimeline>
            <S t="150220548414304" d="288000" r="4501"/>
          </SegmentTimeline>
        </SegmentTemplate>
      </Representation>
      ...
    </AdaptationSet>
    <AdaptationSet id="376" group="2" segmentAlignment="true" startWithSAP="1" contentType="audio" lang="fra">
      <ContentProtection schemeIdUri="urn:uuid:edef8ba9-79d6-4ace-a3c8-27dcd51d21ed"><cenc:pssh>AAAAiHBzc2gAAAAA7e+LqXnWSs6jyCfc1R0h7QAAAGgIARIQH7rsOV0BM3TmyyBxB90bChIQx49W2DcwfMI0bsq178WDyBIQTPw+QqeiYd3hKULfDtNe5BIQEqP6hNUWi6TrJMz14RSXARIQWA0ccDTY6BgjdP53xHdJoBoAKgAyADgASABQAA==</cenc:pssh></ContentProtection>
      <ContentProtection schemeIdUri="urn:mpeg:dash:mp4protection:2011" value="cenc" cenc:default_KID="580d1c70-34d8-e818-2374-fe77c47749a0"/>

      <Role schemeIdUri="urn:mpeg:dash:role:2011" value="main"/>
      <Representation id="376" bandwidth="64000" codecs="mp4a.40.2" mimeType="audio/mp4">
        <SegmentTemplate timescale="90000" initialization="0_1_376_init" media="0_1_376_$Time$">
          <SegmentTimeline>
            <S t="150220548289380" d="288000" r="4502"/>
          </SegmentTimeline>
        </SegmentTemplate>
      </Representation>
    </AdaptationSet>
    ...
  </Period>
</MPD>

La payload de notre loadVideo :

  const options = {
    url,
    transport: 'dash',
    autoPlay: true,
    keySystems: [{
        type: 'widevine',
        fallbackOn: {
          keyInternalError: true,
          keyOutputRestricted: true
        },
        getLicence
    }]
  };
  player.loadVideo(options);
el-gringo commented 1 year ago

After further investigation, I found we are comparing keyIds filled with zero.

Added debug logs in KeySessionRecord.isCompatibleWith

KeySessionRecord.isCompatibleWith areAllKeyIdsContainedIn(keyIds, this._keyIds) false keyIds 4cfc3e42a7a261dde12942df0ed35ee4, 00000000000000000000000000000000 this._keyIds 4cfc3e42a7a261dde12942df0ed35ee4, 580d1c7034d8e8182374fe77c47749a0, 12a3fa84d5168ba4eb24ccf5e1149701, 1fbaec395d013374e6cb207107dd1b0a, c78f56d837307cc2346ecab5efc583c8 [key_session_record.js:142:17](http://localhost:5173/@fs/home/cbosse/Workspaces/player/rx-player/dist/_esm5.processed/core/decrypt/utils/key_session_record.js?t=1669204874185)
KeySessionRecord.isCompatibleWith areAllKeyIdsContainedIn(keyIds, this._initializationData.keyIds) false keyIds 4cfc3e42a7a261dde12942df0ed35ee4, 00000000000000000000000000000000 this._initializationData.keyIds 4cfc3e42a7a261dde12942df0ed35ee4, 580d1c7034d8e8182374fe77c47749a0, 12a3fa84d5168ba4eb24ccf5e1149701, 1fbaec395d013374e6cb207107dd1b0a, c78f56d837307cc2346ecab5efc583c8
el-gringo commented 1 year ago

I have found 2 issues so far.

I have added a check about the keyId. But I am not sure how to delay onInitializationData after keyIds have been associated to the current session.

peaBerberian commented 1 year ago

Hi,

Sorry for the late response. If I follow correctly, you have a content with two keys, one for video and one for audio, yet the RxPlayer re-ask for a key it should already have? Does it stops after a time (for example, once every qualities have been switched to)?

And you think that this is due to the key-id containing all 0? For which PSSH, widevine? That's possible, but I'm unsure of why for now. The MPD also does not list any key-id set to 0 and the RxPlayer does not parse the PSSH for DASH contents, so I'm unsure of how it can know about that 0 key-id before creating a MediaKeySession here.

You're right to look around the KeySessionRecord concept as it is the element storing which key(s) a MediaKeySession is linked to. If MediaKeySession are re-created, it should be because an old one was thought to be incompatible with it and we find out this mainly by checking its KeySessionRecord's isCompatibleWith method.

el-gringo commented 1 year ago

The zero keyId doesn't come from PSSH from DASH, but from the init MP4 segment.

Yes we have keyId for each AdaptationSet type. RxPlayer doesn't ask for a for a key but plays fine now that I have fixed the zero filled keyId issue. However it triggers 3 license-request to our Widevine server instead of 1, same forlicence-renewal.

My understading is that we have to discover all manisfest keyIds before trying to create a new session.

lfaureyt commented 1 year ago

Have you set singleLicensePer to "content" in loadVideo() keySystems options ? If not, it defaults to "init-data" and you get one license request per content key, here 2. Of the 3 license requests, the 1st one occurring may be the initial request for a Widevine service certificate, which you can avoid I think by setting it statically through the serverCertificate option.

el-gringo commented 1 year ago

Yes I tried it, and it leads me to another issue: API: The player stopped because of an error TypeError: chosenRepFromBandwidth is undefined. I have not investigated further.