-
Notifications
You must be signed in to change notification settings - Fork 2.7k
Fix PlayReady key endianness #7510
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
Changes from 2 commits
d46a6df
90a1552
ca9f066
a9e646e
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change | ||||||||||||||||||||||||||||||||||||||||||||||||||
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
|
|
@@ -12,6 +12,7 @@ | |||||||||||||||||||||||||||||||||||||||||||||||||||
| removeEventListener, | ||||||||||||||||||||||||||||||||||||||||||||||||||||
| } from '../utils/event-listener-helper'; | ||||||||||||||||||||||||||||||||||||||||||||||||||||
| import { arrayToHex } from '../utils/hex'; | ||||||||||||||||||||||||||||||||||||||||||||||||||||
| import { changeEndianness } from '../utils/keysystem-util'; | ||||||||||||||||||||||||||||||||||||||||||||||||||||
| import { Logger } from '../utils/logger'; | ||||||||||||||||||||||||||||||||||||||||||||||||||||
| import { | ||||||||||||||||||||||||||||||||||||||||||||||||||||
| getKeySystemsForConfig, | ||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
@@ -881,6 +882,10 @@ | |||||||||||||||||||||||||||||||||||||||||||||||||||
| const sessionLevelKeyId = arrayToHex( | ||||||||||||||||||||||||||||||||||||||||||||||||||||
| new Uint8Array(mediaKeySessionContext.decryptdata.keyId || []), | ||||||||||||||||||||||||||||||||||||||||||||||||||||
| ); | ||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||
| let hasMatchedKey = false; | ||||||||||||||||||||||||||||||||||||||||||||||||||||
| const keyStatuses: { status: MediaKeyStatus; keyId: string }[] = []; | ||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||
| mediaKeySessionContext.mediaKeysSession.keyStatuses.forEach( | ||||||||||||||||||||||||||||||||||||||||||||||||||||
| (status: MediaKeyStatus, keyId: BufferSource) => { | ||||||||||||||||||||||||||||||||||||||||||||||||||||
| // keyStatuses.forEach is not standard API so the callback value looks weird on xboxone | ||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
@@ -890,27 +895,65 @@ | |||||||||||||||||||||||||||||||||||||||||||||||||||
| keyId = status; | ||||||||||||||||||||||||||||||||||||||||||||||||||||
| status = temp; | ||||||||||||||||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||||||||||||||||
| const keyIdWithStatusChange = arrayToHex( | ||||||||||||||||||||||||||||||||||||||||||||||||||||
| 'buffer' in keyId | ||||||||||||||||||||||||||||||||||||||||||||||||||||
| ? new Uint8Array(keyId.buffer, keyId.byteOffset, keyId.byteLength) | ||||||||||||||||||||||||||||||||||||||||||||||||||||
| : new Uint8Array(keyId), | ||||||||||||||||||||||||||||||||||||||||||||||||||||
| ); | ||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||
| let keyIdArray: Uint8Array = 'buffer' in keyId | ||||||||||||||||||||||||||||||||||||||||||||||||||||
| ? new Uint8Array(keyId.buffer, keyId.byteOffset, keyId.byteLength) | ||||||||||||||||||||||||||||||||||||||||||||||||||||
| : new Uint8Array(keyId); | ||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||
| // Handle PlayReady little-endian key ID conversion for status comparison only | ||||||||||||||||||||||||||||||||||||||||||||||||||||
| // Don't modify the original key ID from playlist parsing | ||||||||||||||||||||||||||||||||||||||||||||||||||||
| if (mediaKeySessionContext.keySystem === KeySystems.PLAYREADY && keyIdArray.length === 16) { | ||||||||||||||||||||||||||||||||||||||||||||||||||||
| changeEndianness(keyIdArray); | ||||||||||||||||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||
| const keyIdWithStatusChange = arrayToHex(keyIdArray as Uint8Array<ArrayBuffer>); | ||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||
| // Store all key statuses for processing | ||||||||||||||||||||||||||||||||||||||||||||||||||||
| keyStatuses.push({ status, keyId: keyIdWithStatusChange }); | ||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||
| // Error immediately when encountering a key ID with this status again | ||||||||||||||||||||||||||||||||||||||||||||||||||||
| if (status === 'internal-error') { | ||||||||||||||||||||||||||||||||||||||||||||||||||||
| this.bannedKeyIds[keyIdWithStatusChange] = status; | ||||||||||||||||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||
| // Only acknowledge status changes for level-key ID | ||||||||||||||||||||||||||||||||||||||||||||||||||||
| // Check if this key matches the session-level key ID | ||||||||||||||||||||||||||||||||||||||||||||||||||||
| const matched = keyIdWithStatusChange === sessionLevelKeyId; | ||||||||||||||||||||||||||||||||||||||||||||||||||||
| this.log( | ||||||||||||||||||||||||||||||||||||||||||||||||||||
| `${matched ? '' : 'un'}matched key status change "${status}" for keyStatuses keyId: ${keyIdWithStatusChange} session keyId: ${sessionLevelKeyId} uri: ${mediaKeySessionContext.decryptdata.uri}`, | ||||||||||||||||||||||||||||||||||||||||||||||||||||
| ); | ||||||||||||||||||||||||||||||||||||||||||||||||||||
| if (matched) { | ||||||||||||||||||||||||||||||||||||||||||||||||||||
| hasMatchedKey = true; | ||||||||||||||||||||||||||||||||||||||||||||||||||||
| mediaKeySessionContext.keyStatus = status; | ||||||||||||||||||||||||||||||||||||||||||||||||||||
| this.log( | ||||||||||||||||||||||||||||||||||||||||||||||||||||
| `matched key status change "${status}" for keyStatuses keyId: ${keyIdWithStatusChange} session keyId: ${sessionLevelKeyId} uri: ${mediaKeySessionContext.decryptdata.uri}`, | ||||||||||||||||||||||||||||||||||||||||||||||||||||
| ); | ||||||||||||||||||||||||||||||||||||||||||||||||||||
| } else { | ||||||||||||||||||||||||||||||||||||||||||||||||||||
| this.log( | ||||||||||||||||||||||||||||||||||||||||||||||||||||
| `unmatched key status change "${status}" for keyStatuses keyId: ${keyIdWithStatusChange} session keyId: ${sessionLevelKeyId} uri: ${mediaKeySessionContext.decryptdata.uri}`, | ||||||||||||||||||||||||||||||||||||||||||||||||||||
| ); | ||||||||||||||||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||||||||||||||||
| }, | ||||||||||||||||||||||||||||||||||||||||||||||||||||
| ); | ||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||
| // Handle case where no keys matched but all have the same status | ||||||||||||||||||||||||||||||||||||||||||||||||||||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Hey @ShubhamSharma2311 how can we reproduce this? not sure this is required.
Incorrect:
Collaborator
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. It's changed when read from the PlayReady EXT-X-KEY, so it needs to be changes here too to match: hls.js/src/utils/mediakeys-helper.ts Lines 181 to 205 in 665ea29
Collaborator
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I plan to merge these changes. I have another PR in the works to improve Multi-key handling (group playlist key data with different key-is in the same session). I'll share more soon.
Contributor
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. sir, now I have to fix this thing #7510 (comment). am I right ??
Collaborator
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
No. These changes have been merged. New changes are in review with #7517. Please leave review comments on #7517. This PR can be tested in the branch preview: https://bugfix-eme-multi-key.hls-js-4zn.pages.dev/demo/. Or, file new issues against latest dev/canary at https://hlsjs-dev.video-dev.org/demo/. |
||||||||||||||||||||||||||||||||||||||||||||||||||||
| // This can happen with PlayReady when key IDs don't align properly | ||||||||||||||||||||||||||||||||||||||||||||||||||||
| if (!hasMatchedKey && keyStatuses.length > 0) { | ||||||||||||||||||||||||||||||||||||||||||||||||||||
| const firstStatus = keyStatuses[0].status; | ||||||||||||||||||||||||||||||||||||||||||||||||||||
| const allSameStatus = !keyStatuses.some(({ status }) => status !== firstStatus); | ||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||
| if (allSameStatus && (firstStatus === 'usable' || firstStatus.startsWith('usable'))) { | ||||||||||||||||||||||||||||||||||||||||||||||||||||
| this.log( | ||||||||||||||||||||||||||||||||||||||||||||||||||||
| `No key matched session keyId ${sessionLevelKeyId}, but all keys have usable status "${firstStatus}". Treating as usable.`, | ||||||||||||||||||||||||||||||||||||||||||||||||||||
| ); | ||||||||||||||||||||||||||||||||||||||||||||||||||||
| mediaKeySessionContext.keyStatus = firstStatus; | ||||||||||||||||||||||||||||||||||||||||||||||||||||
| } else if (allSameStatus && (firstStatus === 'internal-error' || firstStatus === 'expired')) { | ||||||||||||||||||||||||||||||||||||||||||||||||||||
| this.log( | ||||||||||||||||||||||||||||||||||||||||||||||||||||
| `No key matched session keyId ${sessionLevelKeyId}, but all keys have error status "${firstStatus}". Applying to session.`, | ||||||||||||||||||||||||||||||||||||||||||||||||||||
| ); | ||||||||||||||||||||||||||||||||||||||||||||||||||||
| mediaKeySessionContext.keyStatus = firstStatus; | ||||||||||||||||||||||||||||||||||||||||||||||||||||
| } else { | ||||||||||||||||||||||||||||||||||||||||||||||||||||
| this.log( | ||||||||||||||||||||||||||||||||||||||||||||||||||||
| `No key matched session keyId ${sessionLevelKeyId}. Key statuses: ${keyStatuses.map(({ keyId, status }) => `${keyId}:${status}`).join(', ')}`, | ||||||||||||||||||||||||||||||||||||||||||||||||||||
| ); | ||||||||||||||||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||
| private fetchServerCertificate( | ||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||
Uh oh!
There was an error while loading. Please reload this page.