-
Notifications
You must be signed in to change notification settings - Fork 72
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
fix: excessive requests when using HDCP fallback with LDL #225
Changes from 14 commits
9d3ec26
baa09f2
b1e0e2b
0e81954
d488416
e99939f
c664833
0ea624e
dd7caf3
dae6b84
8ad96b1
b6418a9
e774014
65453bd
3ba8ce3
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 |
---|---|---|
|
@@ -125,20 +125,43 @@ export const makeNewRequest = (player, requestOptions) => { | |
keySystem | ||
} = requestOptions; | ||
|
||
let timeElapsed = 0; | ||
let pauseTimer; | ||
|
||
player.on('pause', () => { | ||
if (options.limitRenewalsOnPauseTime && typeof options.limitRenewalsOnPauseTime === 'number') { | ||
|
||
pauseTimer = setInterval(() => { | ||
timeElapsed++; | ||
if (timeElapsed >= options.limitRenewalsOnPauseTime) { | ||
clearInterval(pauseTimer); | ||
} | ||
}, 1000); | ||
|
||
player.on('play', () => { | ||
clearInterval(pauseTimer); | ||
timeElapsed = 0; | ||
}); | ||
} | ||
}); | ||
|
||
try { | ||
const keySession = mediaKeys.createSession(); | ||
|
||
safeTriggerOnEventBus(eventBus, { | ||
type: 'keysessioncreated', | ||
keySession | ||
}); | ||
|
||
player.on('dispose', () => { | ||
const closeAndRemoveSession = () => { | ||
keySession.close().then(() => { | ||
|
||
// Because close() is async, this promise could resolve after the | ||
// player has been disposed. | ||
if (eventBus.isDisposed()) { | ||
return; | ||
} | ||
|
||
safeTriggerOnEventBus(eventBus, { | ||
type: 'keysessionclosed', | ||
keySession | ||
}); | ||
removeSession(initData); | ||
}).catch((error) => { | ||
const metadata = { | ||
errorType: EmeError.EMEFailedToCloseSession, | ||
|
@@ -147,6 +170,15 @@ export const makeNewRequest = (player, requestOptions) => { | |
|
||
emeError(error, metadata); | ||
}); | ||
}; | ||
|
||
safeTriggerOnEventBus(eventBus, { | ||
type: 'keysessioncreated', | ||
keySession | ||
}); | ||
|
||
player.on('dispose', () => { | ||
closeAndRemoveSession(); | ||
}); | ||
|
||
return new Promise((resolve, reject) => { | ||
|
@@ -160,6 +192,19 @@ export const makeNewRequest = (player, requestOptions) => { | |
return; | ||
} | ||
|
||
if (event.messageType === 'license-renewal') { | ||
const limitRenewalsBeforePlay = options.limitRenewalsBeforePlay; | ||
const limitRenewalsOnPauseTime = options.limitRenewalsOnPauseTime; | ||
|
||
if (!player.hasStarted() && limitRenewalsBeforePlay || | ||
(player.paused() && typeof limitRenewalsOnPauseTime === 'number' && timeElapsed >= limitRenewalsOnPauseTime) || | ||
player.ended()) { | ||
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. nit: I would split it into several consts eg: 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. Done! |
||
|
||
closeAndRemoveSession(); | ||
return; | ||
} | ||
} | ||
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. This is an attempt to reduce the number of license renewals made when the player is idle (before playback begins) or when the player is paused for too long. A more ideal solution to this would probably be closing and removing the sessions if the player is abandoned in a paused state and then making a new request when unpaused. Maybe such a behavior should be behind an option where the amount of time to wait (before closing and removing the session) is specified ? But this might introduce some complications. 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 seems the 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. To provide further context to what I'm asking here, I'm curious if the player sits either paused or before starting playback for the duration of a license and it expires, will we request a new license and recover upon resuming playback? 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. Made some changes to the previous logic. I've added an option The player will request new license when playback resumes. Session is closed when license-renewal is attempted after playback ends. New requests will be made on 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. Maybe it should be something like: |
||
|
||
getLicense(options, event.message, contentId) | ||
.then((license) => { | ||
resolve(keySession.update(license).then(() => { | ||
|
@@ -231,29 +276,7 @@ export const makeNewRequest = (player, requestOptions) => { | |
if (expired) { | ||
// Close session and remove it from the session list to ensure that a new | ||
// session can be created. | ||
videojs.log.debug('Session expired, closing the session.'); | ||
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. We removed this debug statement and didn't add it back in 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. Good catch! Added it back in |
||
keySession.close().then(() => { | ||
|
||
// Because close() is async, this promise could resolve after the | ||
// player has been disposed. | ||
if (eventBus.isDisposed()) { | ||
return; | ||
} | ||
|
||
safeTriggerOnEventBus(eventBus, { | ||
type: 'keysessionclosed', | ||
keySession | ||
}); | ||
removeSession(initData); | ||
makeNewRequest(player, requestOptions); | ||
}).catch((error) => { | ||
const metadata = { | ||
errorType: EmeError.EMEFailedToCloseSession, | ||
keySystem | ||
}; | ||
|
||
emeError(error, metadata); | ||
}); | ||
closeAndRemoveSession(); | ||
} | ||
}, false); | ||
|
||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -51,7 +51,7 @@ export const removeSession = (sessions, initData) => { | |
} | ||
}; | ||
|
||
export const handleEncryptedEvent = (player, event, options, sessions, eventBus, emeError) => { | ||
export function handleEncryptedEvent(player, event, options, sessions, eventBus, emeError) { | ||
if (!options || !options.keySystems) { | ||
// return silently since it may be handled by a different system | ||
return Promise.resolve(); | ||
|
@@ -89,7 +89,6 @@ export const handleEncryptedEvent = (player, event, options, sessions, eventBus, | |
} | ||
|
||
sessions.push({ initData }); | ||
|
||
return standard5July2016({ | ||
player, | ||
video: event.target, | ||
|
@@ -109,7 +108,7 @@ export const handleEncryptedEvent = (player, event, options, sessions, eventBus, | |
|
||
emeError(error, metadata); | ||
}); | ||
}; | ||
} | ||
|
||
export const handleWebKitNeedKeyEvent = (event, options, eventBus, emeError) => { | ||
if (!options.keySystems || !options.keySystems[LEGACY_FAIRPLAY_KEY_SYSTEM] || !event.initData) { | ||
|
@@ -256,6 +255,37 @@ const onPlayerReady = (player, emeError) => { | |
setupSessions(player); | ||
|
||
if (window.MediaKeys) { | ||
const sendMockEncryptedEvent = () => { | ||
const mockEncryptedEvent = { | ||
initDataType: 'cenc', | ||
initData: null, | ||
target: player.tech_.el_ | ||
}; | ||
|
||
setupSessions(player); | ||
handleEncryptedEvent(player, mockEncryptedEvent, getOptions(player), player.eme.sessions, player.tech_, emeError); | ||
}; | ||
|
||
if (videojs.browser.IS_FIREFOX) { | ||
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. Would a comment describing why we are doing this only on Firefox be useful? I guess at first glance it isn't obvious why we are only sending the mock encrypted event in Firefox 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. Added a comment! |
||
let handled; | ||
|
||
player.on('ended', () =>{ | ||
handled = false; | ||
player.one(['seek', 'play'], (e) => { | ||
if (!handled && player.eme.sessions.length === 0) { | ||
sendMockEncryptedEvent(); | ||
handled = true; | ||
} | ||
}); | ||
}); | ||
player.on('play', () => { | ||
if (player.eme.sessions.length === 0) { | ||
handled = true; | ||
sendMockEncryptedEvent(); | ||
} | ||
}); | ||
} | ||
|
||
// Support EME 05 July 2016 | ||
// Chrome 42+, Firefox 47+, Edge, Safari 12.1+ on macOS 10.14+ | ||
player.tech_.el_.addEventListener('encrypted', (event) => { | ||
|
@@ -306,7 +336,6 @@ const onPlayerReady = (player, emeError) => { | |
*/ | ||
const eme = function(options = {}) { | ||
const player = this; | ||
|
||
const emeError = emeErrorHandler(player); | ||
|
||
player.ready(() => onPlayerReady(player, emeError)); | ||
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Do we need to document the
limitRenewalsBeforePlay
andlimitRenewalsOnPauseTime
in the README?There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Updated README in the latest commit!