Orange-OpenSource / hasplayer.js

Http Adaptive Streaming javascript player based on HTML5 premium extensions (MSE/EME)

Geek Repo:Geek Repo

Github PK Tool:Github PK Tool

how to setup persistent-license support

JosePedroDiasSky opened this issue · comments

The Chrome desktop's Widevine CDM doesn't yet support persistent-license support, as we all know (even though its imminent). Android and ChromeOS do, AFAIK.

Granted I have a CDM with persistent-license support and the messages sent to the widevine service are ok, how do I prevent hasPlayer from requesting the license to the service the second time around?

const p = new MediaPlayer();
  p.init(v);
  p.setConfig({
    'Protection.licensePersistence' : true
  });

  p.addEventListener('error', function(err) {
    console.error(err);
  });

  p.addEventListener('warning', function(err) {
    console.warn(err);
  });

  p.load({
    url : man,
    protData : {
      'com.widevine.alpha' : {
        laURL             : laURL,
        pssh              : PSSH,
        serverCertificate : UAT_SRV_CERT,
        sessionType : 'persistent-license'
      }
    }
  });

This code works just fine with an internet connection. But once I go offline (with a local manifest and chunks) the player still requests laURL. What am I missing?

Hi,
I would say that the player requests the license only if the CDM ask for it.
Have you checked with the EME Logger? Does the CDM sends a 'message' event?

I'm in electron. Trying to repackage eme-logger to be able to use it there. Will report once I do. Thank you.

chrome extensions aren't trivial to use in electron. Since this one doesn't do rocket science, I managed to convert it into a single script logging strings to a global log() function. You can use it too here

This is the output:

{"title":"RequestMediaKeySystemAccessCall","names":["keySystem","supportedConfigurations","target","returned"],"values":["com.widevine.alpha",{"0":{"initDataTypes":["cenc"],"audioCapabilities":[{"contentType":"audio/mp4;codecs=\"mp4a.40.2\"","robustness":"SW_SECURE_CRYPTO"}],"videoCapabilities":[{"contentType":"video/mp4;codecs=\"avc1.4d401e\"","robustness":"SW_SECURE_CRYPTO"}]},"type":"Array"},"Navigator",{"type":"Promise"}]}
{"title":"RequestMediaKeySystemAccessCall Promise Result","names":["status","result"],"values":["resolved",{"type":"MediaKeySystemAccess"}]}
{"title":"RequestMediaKeySystemAccessCall","names":["keySystem","supportedConfigurations","target","returned"],"values":["com.microsoft.playready",{"0":{"initDataTypes":["cenc"],"audioCapabilities":[{"contentType":"audio/mp4;codecs=\"mp4a.40.2\"","robustness":"SW_SECURE_CRYPTO"}],"videoCapabilities":[{"contentType":"video/mp4;codecs=\"avc1.4d401f\"","robustness":"SW_SECURE_CRYPTO"}],"distinctiveIdentifier":"optional","persistentState":"optional","sessionTypes":["temporary"]},"type":"Array"},"Navigator",{"type":"Promise"}]}
{"title":"RequestMediaKeySystemAccessCall Promise Result","names":["status","result"],"values":["rejected",{"type":"DOMException"}]}
{"title":"RequestMediaKeySystemAccessCall","names":["keySystem","supportedConfigurations","target","returned"],"values":["com.widevine.alpha",{"0":{"initDataTypes":["cenc"],"audioCapabilities":[{"contentType":"audio/mp4;codecs=\"mp4a.40.2\"","robustness":"SW_SECURE_CRYPTO"}],"videoCapabilities":[{"contentType":"video/mp4;codecs=\"avc1.4d401f\"","robustness":"SW_SECURE_CRYPTO"}],"distinctiveIdentifier":"optional","persistentState":"required","sessionTypes":["persistent-license"]},"type":"Array"},"Navigator",{"type":"Promise"}]}
{"title":"RequestMediaKeySystemAccessCall Promise Result","names":["status","result"],"values":["resolved",{"type":"MediaKeySystemAccess"}]}
{"title":"GetConfigurationCall","names":["target","returned"],"values":["MediaKeySystemAccess",{"type":"Object","audioCapabilities":[{"contentType":"audio/mp4;codecs=\"mp4a.40.2\"","robustness":"SW_SECURE_CRYPTO"}],"distinctiveIdentifier":"not-allowed","initDataTypes":["cenc"],"label":"","persistentState":"required","sessionTypes":["persistent-license"],"videoCapabilities":[{"contentType":"video/mp4;codecs=\"avc1.4d401f\"","robustness":"SW_SECURE_CRYPTO"}]}]}
{"title":"CreateMediaKeysCall","names":["target","returned"],"values":["MediaKeySystemAccess",{"type":"Promise"}]}
{"title":"CreateMediaKeysCall Promise Result","names":["status","result"],"values":["resolved",{"type":"MediaKeys"}]}
{"title":"SetMediaKeysCall","names":["MediaKeys","target","returned"],"values":[{"type":"MediaKeys","keySystem_":"com.widevine.alpha","listenersAdded_":true},"HTMLVideoElement",{"type":"Promise"}]}
{"title":"SetMediaKeysCall Promise Result","names":["status","result"],"values":["resolved",null]}
{"title":"SetServerCertificateCall","names":["serverCertificate","target","returned"],"values":[{"type":"ArrayBuffer"},"MediaKeys",{"type":"Promise"}]}
{"title":"SetServerCertificateCall Promise Result","names":["status","result"],"values":["resolved",true]}
{"title":"CreateSessionCall","names":["sessionType","target","returned"],"values":["persistent-license","MediaKeys",{"type":"MediaKeySession"}]}
{"title":"GenerateRequestCall","names":["initDataType","initData","target","returned"],"values":["cenc",{"type":"ArrayBuffer"},"MediaKeySession",{"type":"Promise"}]}
{"title":"encryptedEvent","names":["type","time","event","target element"],"values":["encrypted","Thu Jan 01 1970 00:00:00 GMT+0000 (BST)","MediaEncryptedEvent","HTMLVideoElement"]}
{"title":"RequestMediaKeySystemAccessCall","names":["keySystem","supportedConfigurations","target","returned"],"values":["com.widevine.alpha",{"0":{"initDataTypes":["cenc"],"audioCapabilities":[{"contentType":"audio/mp4;codecs=\"mp4a.40.2\"","robustness":"SW_SECURE_CRYPTO"}],"videoCapabilities":[{"contentType":"video/mp4;codecs=\"avc1.4d401f\"","robustness":"SW_SECURE_CRYPTO"}],"distinctiveIdentifier":"optional","persistentState":"required","sessionTypes":["persistent-license"]},"type":"Array"},"Navigator",{"type":"Promise"}]}
{"title":"RequestMediaKeySystemAccessCall Promise Result","names":["status","result"],"values":["resolved",{"type":"MediaKeySystemAccess"}]}
{"title":"GetConfigurationCall","names":["target","returned"],"values":["MediaKeySystemAccess",{"type":"Object","audioCapabilities":[{"contentType":"audio/mp4;codecs=\"mp4a.40.2\"","robustness":"SW_SECURE_CRYPTO"}],"distinctiveIdentifier":"not-allowed","initDataTypes":["cenc"],"label":"","persistentState":"required","sessionTypes":["persistent-license"],"videoCapabilities":[{"contentType":"video/mp4;codecs=\"avc1.4d401f\"","robustness":"SW_SECURE_CRYPTO"}]}]}
{"title":"encryptedEvent","names":["type","time","event","target element"],"values":["encrypted","Thu Jan 01 1970 00:00:00 GMT+0000 (BST)","MediaEncryptedEvent","HTMLVideoElement"]}
{"title":"RequestMediaKeySystemAccessCall","names":["keySystem","supportedConfigurations","target","returned"],"values":["com.widevine.alpha",{"0":{"initDataTypes":["cenc"],"audioCapabilities":[{"contentType":"audio/mp4;codecs=\"mp4a.40.2\"","robustness":"SW_SECURE_CRYPTO"}],"videoCapabilities":[{"contentType":"video/mp4;codecs=\"avc1.4d401f\"","robustness":"SW_SECURE_CRYPTO"}],"distinctiveIdentifier":"optional","persistentState":"required","sessionTypes":["persistent-license"]},"type":"Array"},"Navigator",{"type":"Promise"}]}
{"title":"RequestMediaKeySystemAccessCall Promise Result","names":["status","result"],"values":["resolved",{"type":"MediaKeySystemAccess"}]}
{"title":"GetConfigurationCall","names":["target","returned"],"values":["MediaKeySystemAccess",{"type":"Object","audioCapabilities":[{"contentType":"audio/mp4;codecs=\"mp4a.40.2\"","robustness":"SW_SECURE_CRYPTO"}],"distinctiveIdentifier":"not-allowed","initDataTypes":["cenc"],"label":"","persistentState":"required","sessionTypes":["persistent-license"],"videoCapabilities":[{"contentType":"video/mp4;codecs=\"avc1.4d401f\"","robustness":"SW_SECURE_CRYPTO"}]}]}
{"title":"PlayCall","names":["target","returned"],"values":["HTMLVideoElement",{"type":"Promise"}]}
{"title":"playEvent","names":["type","time","event","target element"],"values":["play","Thu Jan 01 1970 00:00:00 GMT+0000 (BST)","Event","HTMLVideoElement"]}
{"title":"GenerateRequestCall Promise Result","names":["status","result"],"values":["resolved",null]}
{"title":"messageEvent","names":["type","time","event","target element"],"values":["message","Thu Jan 01 1970 00:00:00 GMT+0000 (BST)","MediaKeyMessageEvent","MediaKeySession"]}
{"title":"UpdateCall","names":["response","target","returned"],"values":[{"type":"ArrayBuffer"},"MediaKeySession",{"type":"Promise"}]}
{"title":"UpdateCall Promise Result","names":["status","result"],"values":["resolved",null]}
{"title":"keystatuseschangeEvent","names":["type","time","event","target element"],"values":["keystatuseschange","Thu Jan 01 1970 00:00:01 GMT+0000 (BST)","Event","MediaKeySession"]}
{"title":"PlayCall Promise Result","names":["status","result"],"values":["resolved",null]}

The difference from shaka playback with persistent license is that its messageEvent doesn't fire any xhr when playing for offline storage.

hasPlayer fires the messageEvent, tries to contact the license acquisition URL and fails with no internet connection. I'm playing the manifest and chunks from file://. Not sure how shaka sets up its offline manifest (will check!).

Is it up to the client to offer functions for storing and loading the persistent license? If so, how do I expose them in the hasPlayer configuration?

Once the CDM updates the session it exposes a session id.

The latest protection model has a loadKeySession() method which doesn't seem to be called and which should be called to make use of a preexisting persistent license. The relevant session id is available once the session.update() occurs.

  1. I think the sessionId should be made available by subscribing an event;

  2. I think the mediaPlayerInstance.load() should support receiving a session id.


I tried to do it myself but I'm not sure at which stage the load:

  1. I logged the session id on the Most recent EME implementation's protection model's updateKeySession() (let's assume it logged C0C48C4915F9072F7B55A02617BF8492)

  2. I can obtain the protection model by
    protectionModel = mediaPlayerInstance.getVideoModel().system.getObject('protectionModel')

  3. I can then call
    protectionModel.loadKeySession('C0C48C4915F9072F7B55A02617BF8492')
    I'm not sure at which stage I should do it in order to skip the CDM license acquisition call.

Can anyone comment ⬆️ please?

Hi,
We have not yet addressed Widevine persistence licence since it is not yet available on chrome.
Of what I thought I could understand, the persistence of the licence would be managed internally in the CDM like with PlayReady CDM. That is if the CDM has recorded a license, then it would not request again for the license.
We should take a closer look at it maybe in september, but do not hesitate to propose solutions in the meantime if you have some.

Oh, I see. Since you've been making changes to the persistence license scenario in the latest releases I thought you were addressing this already. Shaka player already supports this scenario (which makes sense since it's maintained by Google).

I'll report my findings here if I manage to work it out by myself. Feel free to close the ticket as unsupported.

I see what you meant.
The persitence that has been implemented in hasplayer is an application-level persistence in the sense that the hasplayer.js records the MediaKeySessions during the lifetime of the MediaPlayer instance. Therefore if you keep the same instance of the MediaPlayer to zap between the different streams, then it will ignore the messages from the CDM concerning the MediaKeysSessions for which the license has already been received and usable. It will request the license only once for each content.

Any chance any of you are looking into persistent license support?
What strategy is lacking and ought to be implemented in hasPlayer? I can try to implement it myself but I need a little guidance on which steps need revising and which changes need to be applied in those. Direct comparison of hasPlayer and shaka is a nightmare, I tried that approach already.

We have not yet started looking at persistence management for Widevine.
I do not know exactly what strategy is wrong since we have not yet be able to test with a CDM that supports persistence.

Hi,
Regarding persistent license management, I confirm what you previously said, i.e. you need to:

  • get a persistent license, then store the MediaKeySession session ID
  • then reload the MediaKeySession using load() method with the recorded session ID, while skipping licence acquisition mechanism in the player.

Have you progressed on this topic?

We should provide soon some code to enable acquisition of persistent license and playback of persistent session, at least in dash.js project in which we have integrated support for smooth streaming contents.

FYI, persistent license loading is available in dash.js v2.6.8 (https://github.com/Dash-Industry-Forum/dash.js/releases)

Hello

Trying to play an encrypted stream and although I provide the token I get in 'pssh' this is what I get:

data
:
code
:
"MEDIA_KEYMESSERR_LICENSER_ERROR"
data
:
error
:
code
:
0
message
:
"<?xml version="1.0" encoding="utf-8"?><ERROR><CODE>general-error</CODE><TEXT>The token string: null is invalid.Wrong format.</TEXT></ERROR>"

In dire need of ideas ;)
TIA,
Pedro