UNPKG

shaka-player

Version:
180 lines (138 loc) 4.2 kB
/*! @license * Shaka Player * Copyright 2016 Google LLC * SPDX-License-Identifier: Apache-2.0 */ goog.provide('shaka.polyfill.VideoFrameCallback'); goog.require('shaka.polyfill'); /** * @summary A polyfill for requestVideoFrameCallback. * Uses requestAnimationFrame + getVideoPlaybackQuality. * @export */ shaka.polyfill.VideoFrameCallback = class { /** * Install the polyfill if needed. * @export */ static install() { if (!window.HTMLVideoElement) { return; } // eslint-disable-next-line no-restricted-syntax const proto = HTMLVideoElement.prototype; if (proto.requestVideoFrameCallback) { return; } if (!window.requestAnimationFrame) { return; } if (!proto.getVideoPlaybackQuality) { return; } proto.requestVideoFrameCallback = shaka.polyfill.VideoFrameCallback.requestVideoFrameCallback_; proto.cancelVideoFrameCallback = shaka.polyfill.VideoFrameCallback.cancelVideoFrameCallback_; } /** * @param {!HTMLVideoElement} video * @return {!shaka.polyfill.VideoFrameCallback.CallbackData_} * @private */ static getData_(video) { let data = shaka.polyfill.VideoFrameCallback.callbackMap_.get(video); if (!data) { data = { nextHandle: 1, callbackHandlesToRafIds: new Map(), presentedFrames: 0, }; shaka.polyfill.VideoFrameCallback.callbackMap_.set(video, data); } return data; } /** * @this {HTMLVideoElement} * @param {function(number, ?VideoFrameMetadata)} callback * @return {number} * @private */ static requestVideoFrameCallback_(callback) { /** @type {!HTMLVideoElement} */ const video = this; const data = shaka.polyfill.VideoFrameCallback.getData_(video); const handle = data.nextHandle++; const checkFrame = (now) => { if (!data.callbackHandlesToRafIds.has(handle)) { return; } const quality = video.getVideoPlaybackQuality(); const presentedFrames = quality.totalVideoFrames || 0; if (presentedFrames !== data.presentedFrames) { data.presentedFrames = presentedFrames; /** @type {!VideoFrameMetadata} */ const metadata = /** @type {!VideoFrameMetadata} */ ({ presentationTime: now, expectedDisplayTime: now, mediaTime: video.currentTime, presentedFrames, width: video.videoWidth, height: video.videoHeight, processingDuration: 0, }); data.callbackHandlesToRafIds.delete(handle); callback(now, metadata); return; } const rafId = window.requestAnimationFrame(checkFrame); data.callbackHandlesToRafIds.set(handle, rafId); }; const rafId = window.requestAnimationFrame(checkFrame); data.callbackHandlesToRafIds.set(handle, rafId); return handle; } /** * @this {HTMLVideoElement} * @param {number} handle * @private */ static cancelVideoFrameCallback_(handle) { /** @type {!HTMLVideoElement} */ const video = this; const data = shaka.polyfill.VideoFrameCallback.callbackMap_.get(video); if (!data) { return; } const rafId = data.callbackHandlesToRafIds.get(handle); if (rafId != null) { window.cancelAnimationFrame(rafId); data.callbackHandlesToRafIds.delete(handle); } } }; /** * @typedef {{ * nextHandle: number, * callbackHandlesToRafIds: !Map<number, number>, * presentedFrames: number * }} * @property {number} nextHandle * @property {!Map<number, number>} callbackHandlesToRafIds * Maps requestVideoFrameCallback handles to the corresponding * requestAnimationFrame IDs so they can be cancelled later. * @property {number} presentedFrames * @private */ shaka.polyfill.VideoFrameCallback.CallbackData_; /** * @private {!WeakMap< * !HTMLVideoElement, * !shaka.polyfill.VideoFrameCallback.CallbackData_>} */ shaka.polyfill.VideoFrameCallback.callbackMap_ = new WeakMap(); // Install at a low priority so that other Video polyfills go first. shaka.polyfill.register(shaka.polyfill.VideoFrameCallback.install, -2);