Shaka-player: Fairplay launch the error always "FAILED_TO_GENERATE_LICENSE_REQUEST" but with the Apple demo page it's working

Created on 8 May 2019  路  17Comments  路  Source: google/shaka-player

Have you read the FAQ and checked for duplicate open issues? Yes

What version of Shaka Player are you using? 2.5.0

Can you reproduce the issue with our latest release version? Yes

Can you reproduce the issue with the latest code from master? Yes

Are you using the demo app or your own custom app? Demo app

If custom app, can you reproduce the issue using our demo app?

What browser and OS are you using?
Safari 12.1 over Mojave

For embedded devices (smart TVs, etc.), what model and firmware version are you using?

What are the manifest and license server URIs?

There is a discussion already started by mail with the link

What did you do?

Load a Fairplay stream

What did you expect to happen?
Works without problem

What actually happened?

Captura de pantalla 2019-05-08 a las 13 08 13

but the video works (Note, that the screen is in blank due to Fairplay)

Note: I changed the custom.js in the demo to support Fairplay with the next change:
const commonDrmSystems = new Set([
'com.widevine.alpha',
'com.microsoft.playready',
'com.adobe.primetime',
'com.apple.fps.1_0',
'org.w3.clearkey',
]);

HLS archived bug

All 17 comments

I think you would get this error under any of the following circumstances:

  • You are connected to an external display that is non-HDCP compatible
  • You are making a screen recording
  • You are using Airplay

Are any of these the case?

Not for me, as far as I know. I'm not sure how to check the HDCP compatibility of my monitor, but I would be surprised if it didn't support HDCP. Definitely not screen recording or using Airplay.

I've had the HDCP problem many times. Is the MediaKeySession publishing webkitkeyerror? If so I presume the Fairplay CDM is enforcing some content restriction, which could be dictated by the license. What if you restrict playback to the lowest variant? Maybe it's a question of constraints on SD/HD/UHD quality content. Just an idea.

Good idea. Yes, we're seeing webkitkeyerror. I'll try SD and see what that gets me.

Oh, wait, actually, it's native HLS, so we have no control over which resolution is played.

It is not any of those circumstances. I am only with the screen of the notebook and nothing else is connected. Nor am I using Airplay.

Note: with the Apple demo page, there are not error.

I'm having same trouble with below error.

Screen Shot 2019-05-14 at 6 05 42 PM

c is null inside this.mediaKeys
```
function Wm(b) {
var c = this.mediaKeys.c,
d = new Event("encrypted");
d.initDataType = "cenc";
d.initData = Zm(b.initData, c);
this.dispatchEvent(d)
}

@the6thm0nth You need to set a server certificate to use FairPlay. See https://github.com/google/shaka-player/issues/382#issuecomment-490953057.

We can do better on missing certs. A lack of server certificate should generate a meaningful error code. I filed #1940 for that.

@TheModMaker thank you, I set on advanced as the guide, but I missed to fetch cert data.

I've tried again with correct advanced drm config, but I got an error with code 6010.

See #1951 for plans to remove FairPlay formatting, which should help. This issue will be closed only when we can confirm that we are able to play @avelad's test content with custom request/response filters.

Thanks @joeyparrish , I'll follow https://github.com/google/shaka-player/issues/1951

Having just integrated against shaka 2.5.2 I can confirm this is an issue and hope that the following debug helps. This is in Safari in Mojave.

For a single key encrypted fairplay asset there are two sessions created, one with the correct initData and second one containing skd://uuid as the initData.

[Log] PatchedMediaKeysApple.onWebkitNeedKey_ ? WebKitMediaKeyNeededEvent {isTrusted: true, initData: Uint8Array, type: "webkitneedkey", ?} (patchedmediakeys_apple.js, line 611)
WebKitMediaKeyNeededEvent {isTrusted: true, initData: Uint8Array, type: "webkitneedkey", target: <video id="video">, currentTarget: <video id="video">, ?}WebKitMediaKeyNeededEventbubbles: falsecancelBubble: falsecancelable: falsecomposed: falsecurrentTarget: nulldefaultPrevented: falseeventPhase: 0initData: Uint8Array [84, 0, 0, 0, 115, 0, 107, 0, 100, 0, ?]Uint8Array (88)isTrusted: truereturnValue: truesrcElement: <video id="video"><video id="video">target: <video id="video"><video id="video">timeStamp: 1554type: "webkitneedkey"WebKitMediaKeyNeededEvent Prototype
[Debug] Creating new temporary session (drm_engine.js, line 1093)
[Log] PatchedMediaKeysApple.MediaKeys.createSession (patchedmediakeys_apple.js, line 324)
[Log] PatchedMediaKeysApple.MediaKeySession (patchedmediakeys_apple.js, line 408)
[Log] PatchedMediaKeysApple.MediaKeySession.generateRequest (patchedmediakeys_apple.js, line 448)
[Log] MyDebug: initData: Tskd://3e44...etc             (patchedmediakeys_apple.js, line 450)                    <= CORRECT
[Log] MyDebug: sessionId: 776d1488-e456-4859-aa7a-afe4c6e5eaeb (patchedmediakeys_apple.js, line 460)
[Debug] Key status changed for session - "" (drm_engine.js, line 1400)
[Debug] Creating new temporary session (drm_engine.js, line 1093)
[Log] PatchedMediaKeysApple.MediaKeys.createSession (patchedmediakeys_apple.js, line 324)
[Log] PatchedMediaKeysApple.MediaKeySession (patchedmediakeys_apple.js, line 408)
[Log] PatchedMediaKeysApple.MediaKeySession.generateRequest (patchedmediakeys_apple.js, line 448)
[Log] MyDebug: initData: skd://3e44d6d7-15a8-45c6-b4ec-c53c76fb70a7 (patchedmediakeys_apple.js, line 450)       <= INCORRECT
[Log] MyDebug: sessionId: da9a72b6-8a50-4210-9ad9-040f6db51727 (patchedmediakeys_apple.js, line 460)

The second session then gives the following error which results in the "FAILED_TO_GENERATE_LICENSE_REQUEST" error we are seeing.

[Log] PatchedMediaKeysApple.onWebkitKeyError_ - Event {isTrusted: true, type: "webkitkeyerror", target: WebKitMediaKeySession, ?} (patchedmediakeys_apple.js, line 705)
Event {isTrusted: true, type: "webkitkeyerror", target: WebKitMediaKeySession, currentTarget: WebKitMediaKeySession, eventPhase: 2, ?}Eventbubbles: falsecancelBubble: falsecancelable: falsecomposed: falsecurrentTarget: nulldefaultPrevented: falseeventPhase: 0isTrusted: truereturnValue: truesrcElement: WebKitMediaKeySession {error: WebKitMediaKeyError, keySystem: "com.apple.fps.1_0", sessionId: "da9a72b6-8a50-4210-9ad9-040f6db51727", onwebkitkeyadded: null, onwebkitkeyerror: null, ?}WebKitMediaKeySessiontarget: WebKitMediaKeySession {error: WebKitMediaKeyError, keySystem: "com.apple.fps.1_0", sessionId: "da9a72b6-8a50-4210-9ad9-040f6db51727", onwebkitkeyadded: null, onwebkitkeyerror: null, ?}WebKitMediaKeySessiontimeStamp: 1578type: "webkitkeyerror"Event Prototype
[Log] MyDebug: sessionId: da9a72b6-8a50-4210-9ad9-040f6db51727 (patchedmediakeys_apple.js, line 706)

The first session created (with the correct initData) completes and the content plays.

[Info] LicenseRequest Success - Updating CDM (drm_engine.js, line 1209)
[Log] PatchedMediaKeysApple.MediaKeySession.update (patchedmediakeys_apple.js, line 493)
[Log] PatchedMediaKeysApple.onWebkitKeyAdded_ ? Event {isTrusted: true, type: "webkitkeyadded", target: WebKitMediaKeySession, ?} (patchedmediakeys_apple.js, line 679)
Event {isTrusted: true, type: "webkitkeyadded", target: WebKitMediaKeySession, currentTarget: WebKitMediaKeySession, eventPhase: 2, ?}Event

After investigating the problem thoroughly, the problem is related to the construction of initData.

Following the example of Apple:
FPS_in_Safari_Example.html.zip

They are using the next function

function onneedkey(event) {
    var video = event.target;
    var initData = event.initData;
    var contentId = extractContentId(initData);
    initData = concatInitDataIdAndCertificate(initData, contentId, certificate);

    if (!video.webkitKeys)
    {
        selectKeySystem();
        video.webkitSetMediaKeys(new WebKitMediaKeys(keySystem));
    }

    if (!video.webkitKeys)
        throw "Could not create MediaKeys";

    var keySession = video.webkitKeys.createSession("video/mp4", initData);
    if (!keySession)
        throw "Could not create key session";

    keySession.contentId = contentId;
    waitForEvent('webkitkeymessage', licenseRequestReady, keySession);
    waitForEvent('webkitkeyadded', onkeyadded, keySession);
    waitForEvent('webkitkeyerror', onkeyerror, keySession);
}

For the initData there are a previous processing with the contentId:

var initData = event.initData;
var contentId = extractContentId(initData);
initData = concatInitDataIdAndCertificate(initData, contentId, certificate);

And the contentId use the next function:

function extractContentId(initData) {
    contentId = arrayToString(initData);
    // contentId is passed up as a URI, from which the host must be extracted:
    var link = document.createElement('a');
    link.href = contentId;
    return link.hostname;
}

function arrayToString(array) {
    var uint16array = new Uint16Array(array.buffer);
    return String.fromCharCode.apply(null, uint16array);
}

Note: the function extractContentId must be customizable by the apps (eg: filter) due differents FairPlay servers has different implementation (with our providers we are found 3 implementations differents in 5 providers). It is also necessary to expose the contentID in the request filters, because some servers need it.

Next, it's neccessary append the contentId with the previous initData and the FairPlay certificate, with the function:

function concatInitDataIdAndCertificate(initData, id, cert) {
  if (typeof id == "string")
      id = stringToArray(id);
  // layout is [initData][4 byte: idLength][idLength byte: id][4 byte:certLength][certLength byte: cert]
  var offset = 0;
  var buffer = new ArrayBuffer(initData.byteLength + 4 + id.byteLength + 4 + cert.byteLength);
  var dataView = new DataView(buffer);

  var initDataArray = new Uint8Array(buffer, offset, initData.byteLength);
  initDataArray.set(initData);
  offset += initData.byteLength;

  dataView.setUint32(offset, id.byteLength, true);
  offset += 4;

  var idArray = new Uint16Array(buffer, offset, id.length);
  idArray.set(id);
  offset += idArray.byteLength;

  dataView.setUint32(offset, cert.byteLength, true);
  offset += 4;

  var certArray = new Uint8Array(buffer, offset, cert.byteLength);
  certArray.set(cert);

  return new Uint8Array(buffer, 0, buffer.byteLength);
}

I hope this helps solve the problem.

Now that #1951 is closed, I think you should have everything to customize the Player to do what you want. See the updated tutorial for more info. If you are having any more trouble, feel free to comment or reopen.

Having the same issue as @KieronAllsop. Init data is generated 2 times, first time the DRM flow passes, and second time it throws FAILED_TO_GENERATE_LICENSE_REQUEST error. The stream can be played.
Found out that encrypted event is triggered twice, so if in drm_engine.js I change:

// Explicit init data for any one stream or an offline session is
// sufficient to suppress 'encrypted' events for all streams.
      const cb = (e) => this.newInitData(
          e.initDataType, shaka.util.BufferUtils.toUint8(e.initData));
      this.eventManager_.listen(this.video_, 'encrypted', cb);

to

// Explicit init data for any one stream or an offline session is
// sufficient to suppress 'encrypted' events for all streams.
      const cb = (e) => this.newInitData(
          e.initDataType, shaka.util.BufferUtils.toUint8(e.initData));
      this.eventManager_.listenOnce(this.video_, 'encrypted', cb);

it goes away.
Not sure what this in-code comment means, should this somehow already be suppressed to trigger just once?

Was this page helpful?
0 / 5 - 0 ratings