Skip to content

Commit

Permalink
feat: Detect maximum HW resolution automatically on some platforms (#…
Browse files Browse the repository at this point in the history
  • Loading branch information
avelad authored Feb 1, 2024
1 parent 62ab048 commit 278c7bc
Show file tree
Hide file tree
Showing 11 changed files with 314 additions and 53 deletions.
16 changes: 16 additions & 0 deletions externs/hisense.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
/*! @license
* Shaka Player
* Copyright 2016 Google LLC
* SPDX-License-Identifier: Apache-2.0
*/

/**
* @fileoverview Externs for Hisense
*
* @externs
*/

/**
* @return {boolean}
*/
window.Hisense_Get4KSupportState = function() {};
31 changes: 31 additions & 0 deletions externs/playstation.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
/*! @license
* Shaka Player
* Copyright 2016 Google LLC
* SPDX-License-Identifier: Apache-2.0
*/

/**
* @fileoverview Externs for PlayStation
*
* @externs
*/


/** @const */
var msdk = {};


/** @const */
msdk.device = {};


/**
* @return {!Promise.<{resolution: string}>}
*/
msdk.device.getDisplayInfo = function() {};


/**
* @return {!Promise.<{resolution: string}>}
*/
msdk.device.getDisplayInfoImmediate = function() {};
41 changes: 41 additions & 0 deletions externs/tizen.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
/*! @license
* Shaka Player
* Copyright 2016 Google LLC
* SPDX-License-Identifier: Apache-2.0
*/

/**
* @fileoverview Externs for Tizen
* @externs
*/


/** @const */
var webapis = {};


/** @const */
webapis.systeminfo = {};


/**
* @return {{width: number, height: number}}
*/
webapis.systeminfo.getMaxVideoResolution = function() {};


/** @const */
webapis.productinfo = {};


/**
* @return {boolean}
*/
webapis.productinfo.is8KPanelSupported = function() {};


/**
* @return {boolean}
*/
webapis.productinfo.isUdPanelSupported = function() {};

19 changes: 19 additions & 0 deletions externs/webos.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
/*! @license
* Shaka Player
* Copyright 2016 Google LLC
* SPDX-License-Identifier: Apache-2.0
*/

/**
* @fileoverview Externs for WebOS
*
* @externs
*/


/** @const */
var PalmSystem = {};


/** @type {string} */
PalmSystem.deviceInfo;
43 changes: 43 additions & 0 deletions externs/xbox.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
/*! @license
* Shaka Player
* Copyright 2016 Google LLC
* SPDX-License-Identifier: Apache-2.0
*/

/**
* @fileoverview Externs for Xbox
*
* @externs
*/


/** @const */
var Windows = {};


/** @const */
Windows.Media = {};


/** @const */
Windows.Media.Protection = {};


/** @const */
Windows.Media.Protection.ProtectionCapabilities = class {
/**
* @param {string} type
* @param {string} keySystem
* @return {!Windows.Media.Protection.ProtectionCapabilityResult}
*/
isTypeSupported(type, keySystem) {}
};

/**
* @enum {string}
*/
Windows.Media.Protection.ProtectionCapabilityResult = {
notSupported: 'NotSupported',
maybe: 'Maybe',
probably: 'Probably',
};
14 changes: 0 additions & 14 deletions lib/cast/cast_receiver.js
Original file line number Diff line number Diff line change
Expand Up @@ -287,20 +287,6 @@ shaka.cast.CastReceiver = class extends shaka.util.FakeEventTarget {
this.player_, name, (event) => this.proxyEvent_('player', event));
}

// In our tests, the original Chromecast seems to have trouble decoding
// above 1080p. It would be a waste to select a higher res anyway, given
// that the device only outputs 1080p to begin with.

// Chromecast has an extension to query the device/display's resolution.
if (cast.__platform__ && cast.__platform__.canDisplayType(
'video/mp4; codecs="avc1.640028"; width=3840; height=2160')) {
// The device and display can both do 4k. Assume a 4k limit.
this.player_.setMaxHardwareResolution(3840, 2160);
} else {
// Chromecast has always been able to do 1080p. Assume a 1080p limit.
this.player_.setMaxHardwareResolution(1920, 1080);
}

// Do not start excluding values from update messages until the video is
// fully loaded.
this.eventManager_.listen(this.video_, 'loadeddata', () => {
Expand Down
24 changes: 22 additions & 2 deletions lib/player.js
Original file line number Diff line number Diff line change
Expand Up @@ -1416,6 +1416,9 @@ shaka.Player = class extends shaka.util.FakeEventTarget {
await this.srcEqualsInner_(startTimeOfLoad, mimeType);
}, 'srcEqualsInner_');
} else {
await mutexWrapOperation(async () => {
await this.detectAndSetMaxHardwareResolution_();
}, 'detectAndSetMaxHardwareResolution_');
if (!this.mediaSourceEngine_) {
await mutexWrapOperation(async () => {
await this.initializeMediaSourceEngineInner_();
Expand Down Expand Up @@ -5195,8 +5198,6 @@ shaka.Player = class extends shaka.util.FakeEventTarget {

/**
* Set the maximum resolution that the platform's hardware can handle.
* This will be called automatically by <code>shaka.cast.CastReceiver</code>
* to enforce limitations of the Chromecast hardware.
*
* @param {number} width
* @param {number} height
Expand All @@ -5207,6 +5208,25 @@ shaka.Player = class extends shaka.util.FakeEventTarget {
this.maxHwRes_.height = height;
}

/**
* Detect and set the maximum resolution that the platform's hardware can
* handle.
*
* @private
*/
async detectAndSetMaxHardwareResolution_() {
// Avoid having to detect the resolution again if it has already been
// detected or set
if (this.maxHwRes_.width != Infinity ||
this.maxHwRes_.height != Infinity) {
return;
}
const maxResolution =
await shaka.util.Platform.detectMaxHardwareResolution();
this.maxHwRes_.width = maxResolution.width;
this.maxHwRes_.height = maxResolution.height;
}

/**
* Retry streaming after a streaming failure has occurred. When the player has
* not loaded content or is loading content, this will be a no-op and will
Expand Down
132 changes: 132 additions & 0 deletions lib/util/platform.js
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@

goog.provide('shaka.util.Platform');

goog.require('shaka.log');
goog.require('shaka.util.Timer');


Expand Down Expand Up @@ -550,6 +551,137 @@ shaka.util.Platform = class {

return false;
}

/**
* Detect the maximum resolution that the platform's hardware can handle.
*
* @return {!Promise.<{width: number, height: number}>}
*/
static async detectMaxHardwareResolution() {
const Platform = shaka.util.Platform;
const maxResolution = {
width: Infinity,
height: Infinity,
};

if (Platform.isChromecast()) {
// In our tests, the original Chromecast seems to have trouble decoding
// above 1080p. It would be a waste to select a higher res anyway, given
// that the device only outputs 1080p to begin with.
// Chromecast has an extension to query the device/display's resolution.
if (window.cast && cast.__platform__ &&
cast.__platform__.canDisplayType && cast.__platform__.canDisplayType(
'video/mp4; codecs="avc1.640028"; width=3840; height=2160')) {
// The device and display can both do 4k. Assume a 4k limit.
maxResolution.width = 3840;
maxResolution.height = 2160;
} else {
// Chromecast has always been able to do 1080p. Assume a 1080p limit.
maxResolution.width = 1920;
maxResolution.height = 1080;
}
} else if (Platform.isTizen()) {
maxResolution.width = 1920;
maxResolution.height = 1080;
try {
if (webapis.systeminfo && webapis.systeminfo.getMaxVideoResolution) {
const maxVideoResolution =
webapis.systeminfo.getMaxVideoResolution();
maxResolution.width = maxVideoResolution.width;
maxResolution.height = maxVideoResolution.height;
} else {
if (webapis.productinfo.is8KPanelSupported &&
webapis.productinfo.is8KPanelSupported()) {
maxResolution.width = 7680;
maxResolution.height = 4320;
} else if (webapis.productinfo.isUdPanelSupported &&
webapis.productinfo.isUdPanelSupported()) {
maxResolution.width = 3840;
maxResolution.height = 2160;
}
}
} catch (e) {
shaka.log.alwaysWarn('Tizen: Error detecting screen size, default ' +
'screen size 1920x1080.');
}
} else if (Platform.isXboxOne()) {
const protectionCapabilities =
new Windows.Media.Protection.ProtectionCapabilities();
const protectionResult =
Windows.Media.Protection.ProtectionCapabilityResult;
// isTypeSupported may return "maybe", which means the operation is not
// completed. This means we need to retry
// https://learn.microsoft.com/en-us/uwp/api/windows.media.protection.protectioncapabilityresult?view=winrt-22621
let result = null;
try {
const type =
'video/mp4;codecs="hvc1,mp4a";features="decode-res-x=3840,' +
'decode-res-y=2160,decode-bitrate=20000,decode-fps=30,' +
'decode-bpc=10,display-res-x=3840,display-res-y=2160,' +
'display-bpc=8"';
const keySystem = 'com.microsoft.playready.recommendation';
do {
result = protectionCapabilities.isTypeSupported(type, keySystem);
} while (result === protectionResult.maybe);
} catch (e) {
result = protectionResult.notSupported;
}
if (result === protectionResult.probably) {
maxResolution.width = 3840;
maxResolution.height = 2160;
} else {
maxResolution.width = 1920;
maxResolution.height = 1080;
}
} else if (Platform.isWebOS()) {
try {
const deviceInfo =
/** @type {{screenWidth: number, screenHeight: number}} */(
JSON.parse(window.PalmSystem.deviceInfo));
// WebOS has always been able to do 1080p. Assume a 1080p limit.
maxResolution.width = Math.max(1920, deviceInfo.screenWidth);
maxResolution.height = Math.max(1080, deviceInfo.screenHeight);
} catch (e) {
shaka.log.alwaysWarn('WebOS: Error detecting screen size, default ' +
'screen size 1920x1080.');
maxResolution.width = 1920;
maxResolution.height = 1080;
}
} else if (Platform.isHisense()) {
// eslint-disable-next-line new-cap
if (window.Hisense_Get4KSupportState &&
// eslint-disable-next-line new-cap
window.Hisense_Get4KSupportState()) {
maxResolution.width = 3840;
maxResolution.height = 2160;
} else {
maxResolution.width = 1920;
maxResolution.height = 1080;
}
} else if (Platform.isPS4() || Platform.isPS5()) {
let supports4K = false;
try {
const result = await window.msdk.device.getDisplayInfo();
supports4K = result.resolution === '4K';
} catch (e) {
try {
const result = await window.msdk.device.getDisplayInfoImmediate();
supports4K = result.resolution === '4K';
} catch (e) {
shaka.log.alwaysWarn(
'PlayStation: Failed to get the display info:', e);
}
}
if (supports4K) {
maxResolution.width = 3840;
maxResolution.height = 2160;
} else {
maxResolution.width = 1920;
maxResolution.height = 1080;
}
}
return maxResolution;
}
};

/** @private {shaka.util.Timer} */
Expand Down
11 changes: 9 additions & 2 deletions lib/util/stream_utils.js
Original file line number Diff line number Diff line change
Expand Up @@ -263,13 +263,20 @@ shaka.util.StreamUtils = class {
// |video.width| and |video.height| can be undefined, which breaks
// the math, so make sure they are there first.
if (video && video.width && video.height) {
if (!inRange(video.width,
let videoWidth = video.width;
let videoHeight = video.height;
if (videoHeight > videoWidth) {
// Vertical video.
[videoWidth, videoHeight] = [videoHeight, videoWidth];
}

if (!inRange(videoWidth,
restrictions.minWidth,
Math.min(restrictions.maxWidth, maxHwRes.width))) {
return false;
}

if (!inRange(video.height,
if (!inRange(videoHeight,
restrictions.minHeight,
Math.min(restrictions.maxHeight, maxHwRes.height))) {
return false;
Expand Down
Loading

0 comments on commit 278c7bc

Please sign in to comment.