Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Fairplay launch the error always "FAILED_TO_GENERATE_LICENSE_REQUEST" but with the Apple demo page it's working #1923

Closed
avelad opened this issue May 8, 2019 · 17 comments
Assignees
Labels
component: HLS The issue involves Apple's HLS manifest format status: archived Archived and locked; will not be updated type: bug Something isn't working correctly
Milestone

Comments

@avelad
Copy link
Member

avelad commented May 8, 2019

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',
]);

@avelad avelad changed the title Fairplay launch the error always "FAILED_TO_GENERATE_LICENSE_REQUEST" but with the Apple demo page it working Fairplay launch the error always "FAILED_TO_GENERATE_LICENSE_REQUEST" but with the Apple demo page it's working May 8, 2019
@joeyparrish joeyparrish added type: bug Something isn't working correctly component: HLS The issue involves Apple's HLS manifest format labels May 8, 2019
@shaka-bot shaka-bot added this to the v2.6 milestone May 8, 2019
@joeyparrish joeyparrish self-assigned this May 8, 2019
@chrisfillmore
Copy link
Contributor

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?

@joeyparrish
Copy link
Member

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.

@chrisfillmore
Copy link
Contributor

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.

@joeyparrish
Copy link
Member

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

@joeyparrish
Copy link
Member

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

@avelad
Copy link
Member Author

avelad commented May 9, 2019

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.

@hoony-o-1
Copy link

hoony-o-1 commented May 14, 2019

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)
}

@TheModMaker
Copy link
Contributor

@the6thm0nth You need to set a server certificate to use FairPlay. See #382 (comment).

@joeyparrish
Copy link
Member

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

@hoony-o-1
Copy link

hoony-o-1 commented May 15, 2019

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

@hoony-o-1
Copy link

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

@joeyparrish
Copy link
Member

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.

@avelad
Copy link
Member Author

avelad commented May 20, 2019

Thanks @joeyparrish , I'll follow #1951

@KieronAllsop
Copy link

KieronAllsop commented Jun 24, 2019

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

@avelad
Copy link
Member Author

avelad commented Aug 6, 2019

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.

@TheModMaker
Copy link
Contributor

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.

@bcupac
Copy link

bcupac commented Sep 2, 2019

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?

@shaka-project shaka-project locked and limited conversation to collaborators Oct 7, 2019
@shaka-bot shaka-bot added the status: archived Archived and locked; will not be updated label Apr 15, 2021
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
Labels
component: HLS The issue involves Apple's HLS manifest format status: archived Archived and locked; will not be updated type: bug Something isn't working correctly
Projects
None yet
Development

No branches or pull requests

8 participants