From a583c4a8097942f7ed919d243c3e945d9810fc82 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C3=81lvaro=20Velad=20Galv=C3=A1n?= Date: Mon, 13 Jan 2025 19:01:08 +0100 Subject: [PATCH] feat(ABR): Use PiP window size when using requestPictureInPicture (#7882) Close https://github.com/shaka-project/shaka-player/issues/7872 --- externs/pictureinpicture.js | 40 +++++++++++++++++++++++++++++ lib/abr/simple_abr_manager.js | 47 +++++++++++++++++++++++++++-------- 2 files changed, 77 insertions(+), 10 deletions(-) diff --git a/externs/pictureinpicture.js b/externs/pictureinpicture.js index 47b5db4818..bd3d4d541e 100644 --- a/externs/pictureinpicture.js +++ b/externs/pictureinpicture.js @@ -51,6 +51,46 @@ HTMLMediaElement.prototype.webkitSupportsPresentationMode = function(mode) {}; HTMLMediaElement.prototype.webkitPresentationMode; +/** + * @constructor + * @implements {EventTarget} + */ +function PictureInPictureWindow() {} + + +/** @type {number} */ +PictureInPictureWindow.prototype.width; + + +/** @type {number} */ +PictureInPictureWindow.prototype.height; + + +/** @override */ +PictureInPictureWindow.prototype.addEventListener = + function(type, listener, options) {}; + + +/** @override */ +PictureInPictureWindow.prototype.removeEventListener = + function(type, listener, options) {}; + + +/** @override */ +PictureInPictureWindow.prototype.dispatchEvent = function(event) {}; + + +/** + * @constructor + * @extends {Event} + */ +function PictureInPictureEvent() {} + + +/** @type {PictureInPictureWindow} */ +PictureInPictureEvent.prototype.pictureInPictureWindow; + + /** * @typedef {{ * width: (number|undefined), diff --git a/lib/abr/simple_abr_manager.js b/lib/abr/simple_abr_manager.js index e75cc7be12..74759987f2 100644 --- a/lib/abr/simple_abr_manager.js +++ b/lib/abr/simple_abr_manager.js @@ -137,6 +137,9 @@ shaka.abr.SimpleAbrManager = class { }); } + /** @private {PictureInPictureWindow} */ + this.pictureInPictureWindow_ = null; + /** @private {?shaka.util.CmsdManager} */ this.cmsdManager_ = null; } @@ -161,6 +164,8 @@ shaka.abr.SimpleAbrManager = class { this.resizeObserverTimer_.stop(); + this.pictureInPictureWindow_ = null; + this.cmsdManager_ = null; // Don't reset |startupComplete_|: if we've left the startup interval, we @@ -206,10 +211,15 @@ shaka.abr.SimpleAbrManager = class { if (this.resizeObserver_ && this.config_.restrictToElementSize) { const devicePixelRatio = this.config_.ignoreDevicePixelRatio ? 1 : this.windowToCheck_.devicePixelRatio; - maxHeight = Math.min( - maxHeight, this.mediaElement_.clientHeight * devicePixelRatio); - maxWidth = Math.min( - maxWidth, this.mediaElement_.clientWidth * devicePixelRatio); + let height = this.mediaElement_.clientHeight; + let width = this.mediaElement_.clientWidth; + if (this.pictureInPictureWindow_ && document.pictureInPictureElement && + document.pictureInPictureElement == this.mediaElement_) { + height = this.pictureInPictureWindow_.height; + width = this.pictureInPictureWindow_.width; + } + maxHeight = Math.min(maxHeight, height * devicePixelRatio); + maxWidth = Math.min(maxWidth, width * devicePixelRatio); } let normalVariants = this.variants_.filter((variant) => { @@ -407,15 +417,32 @@ shaka.abr.SimpleAbrManager = class { this.resizeObserver_.disconnect(); this.resizeObserver_ = null; } + const onResize = () => { + const SimpleAbrManager = shaka.abr.SimpleAbrManager; + // Batch up resize changes before checking them. + this.resizeObserverTimer_.tickAfter( + /* seconds= */ SimpleAbrManager.RESIZE_OBSERVER_BATCH_TIME); + }; if (this.mediaElement_ && 'ResizeObserver' in window) { - this.resizeObserver_ = new ResizeObserver(() => { - const SimpleAbrManager = shaka.abr.SimpleAbrManager; - // Batch up resize changes before checking them. - this.resizeObserverTimer_.tickAfter( - /* seconds= */ SimpleAbrManager.RESIZE_OBSERVER_BATCH_TIME); - }); + this.resizeObserver_ = new ResizeObserver(onResize); this.resizeObserver_.observe(this.mediaElement_); } + + this.eventManager_.listen(mediaElement, 'enterpictureinpicture', (e) => { + const event = /** @type {PictureInPictureEvent} */(e); + if (event.pictureInPictureWindow) { + this.pictureInPictureWindow_ = event.pictureInPictureWindow; + this.eventManager_.listen( + this.pictureInPictureWindow_, 'resize', onResize); + } + }); + this.eventManager_.listen(mediaElement, 'leavepictureinpicture', () => { + if (this.pictureInPictureWindow_) { + this.eventManager_.unlisten( + this.pictureInPictureWindow_, 'resize', onResize); + } + this.pictureInPictureWindow_ = null; + }); }