From 71d052857bd199f5a9d802805d8aba700013ca39 Mon Sep 17 00:00:00 2001 From: Santiago Puppo Date: Wed, 3 Dec 2025 11:00:20 -0300 Subject: [PATCH 1/6] Initial implementation --- .../pages/mux-video-react.tsx | 14 ++++++++++- examples/vanilla-ts-esm/public/mux-video.html | 2 ++ packages/mux-player/src/base.ts | 14 +++++++++++ packages/mux-player/src/template.ts | 1 + packages/mux-player/src/types.ts | 1 + packages/mux-video/src/ads/react.ts | 1 + packages/mux-video/src/base.ts | 24 +++++++++++++++++++ packages/mux-video/src/react.ts | 1 + packages/playback-core/src/index.ts | 21 ++++++++++++---- packages/playback-core/src/types.ts | 2 ++ 10 files changed, 76 insertions(+), 5 deletions(-) diff --git a/examples/nextjs-with-typescript/pages/mux-video-react.tsx b/examples/nextjs-with-typescript/pages/mux-video-react.tsx index fd20dd3b9..205877d88 100644 --- a/examples/nextjs-with-typescript/pages/mux-video-react.tsx +++ b/examples/nextjs-with-typescript/pages/mux-video-react.tsx @@ -4,11 +4,13 @@ import MuxVideo from "@mux/mux-video/react"; const INITIAL_AUTOPLAY = false; const INITIAL_MUTED = false; +const INITIAL_CAP_LEVEL_TO_PLAYER_SIZE = false; function MuxVideoPage() { const mediaElRef = useRef(null); const [autoplay, setAutoplay] = useState<"muted" | boolean>(INITIAL_AUTOPLAY); const [muted, setMuted] = useState(INITIAL_MUTED); + const [capLevelToPlayerSize, setCapLevelToPlayerSize] = useState(INITIAL_CAP_LEVEL_TO_PLAYER_SIZE); const [paused, setPaused] = useState(true); return ( @@ -32,12 +34,13 @@ function MuxVideoPage() { // }} // envKey="mux-data-env-key" controls + capLevelToPlayerSize={capLevelToPlayerSize} autoplay={autoplay} muted={muted} maxResolution="2160p" minResolution="540p" renditionOrder="desc" - preferPlayback="native" + preferPlayback="mse" onPlay={() => { setPaused(false); }} @@ -74,6 +77,15 @@ function MuxVideoPage() { checked={muted} /> +
+ + setCapLevelToPlayerSize(!capLevelToPlayerSize)} + checked={capLevelToPlayerSize} + /> +
); diff --git a/examples/vanilla-ts-esm/public/mux-video.html b/examples/vanilla-ts-esm/public/mux-video.html index ac0fa81d0..5bd4df716 100644 --- a/examples/vanilla-ts-esm/public/mux-video.html +++ b/examples/vanilla-ts-esm/public/mux-video.html @@ -40,6 +40,8 @@

Elements

metadata-video-title="Star Wars: Episode 3" metadata-viewer-user-id="user-id-6789" stream-type="on-demand" + cap-level-to-player-size="false" + prefer-playback="mse" controls muted > diff --git a/packages/mux-player/src/base.ts b/packages/mux-player/src/base.ts index fdafe58f7..60677566d 100644 --- a/packages/mux-player/src/base.ts +++ b/packages/mux-player/src/base.ts @@ -92,6 +92,7 @@ const PlayerAttributes = { NO_TOOLTIPS: 'no-tooltips', PROUDLY_DISPLAY_MUX_BADGE: 'proudly-display-mux-badge', DISABLE_PSEUDO_ENDED: 'disable-pseudo-ended', + CAP_LEVEL_TO_PLAYER_SIZE: 'cap-level-to-player-size', } as const; const ThemeAttributeNames = [ @@ -188,6 +189,7 @@ function getProps(el: MuxPlayerElement, state?: any): MuxTemplateProps { proudlyDisplayMuxBadge: el.hasAttribute(PlayerAttributes.PROUDLY_DISPLAY_MUX_BADGE), castReceiver: el.castReceiver, disablePseudoEnded: el.hasAttribute(PlayerAttributes.DISABLE_PSEUDO_ENDED), + capLevelToPlayerSize: el.hasAttribute(PlayerAttributes.CAP_LEVEL_TO_PLAYER_SIZE), ...state, // NOTE: since the attribute value is used as the "source of truth" for the property getter, // moving this below the `...state` spread so it resolves to the default value when unset (CJP) @@ -1883,6 +1885,18 @@ class MuxPlayerElement extends VideoApiElement implements IMuxPlayerElement { this.setAttribute(PlayerAttributes.PROUDLY_DISPLAY_MUX_BADGE, ''); } } + + get capLevelToPlayerSize() { + return this.hasAttribute(PlayerAttributes.CAP_LEVEL_TO_PLAYER_SIZE); + } + + set capLevelToPlayerSize(val: boolean) { + if (!val) { + this.removeAttribute(PlayerAttributes.CAP_LEVEL_TO_PLAYER_SIZE); + return; + } + this.setAttribute(PlayerAttributes.CAP_LEVEL_TO_PLAYER_SIZE, ''); + } } export function getVideoAttribute(el: MuxPlayerElement, name: string) { diff --git a/packages/mux-player/src/template.ts b/packages/mux-player/src/template.ts index cc9bba675..d7c911dda 100644 --- a/packages/mux-player/src/template.ts +++ b/packages/mux-player/src/template.ts @@ -141,6 +141,7 @@ export const content = (props: MuxTemplateProps) => html` exportparts="video" disable-pseudo-ended="${props.disablePseudoEnded ?? false}" max-auto-resolution="${props.maxAutoResolution ?? false}" + cap-level-to-player-size="${props.capLevelToPlayerSize ?? false}" > ${props.storyboard ? html`` diff --git a/packages/mux-player/src/types.ts b/packages/mux-player/src/types.ts index 8e1debe4e..89bcc08b4 100644 --- a/packages/mux-player/src/types.ts +++ b/packages/mux-player/src/types.ts @@ -68,6 +68,7 @@ export type MuxTemplateProps = Partial & { /** Allow playback with ad blocker */ allowAdBlocker?: boolean; disablePseudoEnded?: boolean; + capLevelToPlayerSize?: boolean; }; export type DialogOptions = { diff --git a/packages/mux-video/src/ads/react.ts b/packages/mux-video/src/ads/react.ts index 7aed9e7de..172b29849 100644 --- a/packages/mux-video/src/ads/react.ts +++ b/packages/mux-video/src/ads/react.ts @@ -20,6 +20,7 @@ const ReactPropToAttrNameMap: Record = { playsInline: 'playsinline', disablePictureInPicture: 'disablepictureinpicture', disableRemotePlayback: 'disableremoteplayback', + capLevelToPlayerSize: 'cap-level-to-player-size', }; function toAttributeName(propName: string) { diff --git a/packages/mux-video/src/base.ts b/packages/mux-video/src/base.ts index 7c4f09c0d..09095bffb 100644 --- a/packages/mux-video/src/base.ts +++ b/packages/mux-video/src/base.ts @@ -54,6 +54,7 @@ export * from './types.js'; export const Attributes = { BEACON_COLLECTION_DOMAIN: 'beacon-collection-domain', CUSTOM_DOMAIN: 'custom-domain', + CAP_LEVEL_TO_PLAYER_SIZE: 'cap-level-to-player-size', DEBUG: 'debug', DISABLE_TRACKING: 'disable-tracking', DISABLE_COOKIES: 'disable-cookies', @@ -516,6 +517,20 @@ export class MuxVideoBaseElement extends CustomVideoElement implements IMuxVideo } } + get capLevelToPlayerSize(): boolean | undefined { + let val = this.getAttribute(Attributes.CAP_LEVEL_TO_PLAYER_SIZE); + if (val == null) return undefined; + return val === 'true'; + } + + set capLevelToPlayerSize(val: boolean | undefined) { + if (val !== undefined) { + this.setAttribute(Attributes.CAP_LEVEL_TO_PLAYER_SIZE, val.toString()); + } else { + this.removeAttribute(Attributes.CAP_LEVEL_TO_PLAYER_SIZE); + } + } + get drmToken() { return this.getAttribute(Attributes.DRM_TOKEN) ?? undefined; } @@ -900,6 +915,15 @@ export class MuxVideoBaseElement extends CustomVideoElement implements IMuxVideo } break; } + /*case Attributes.CAP_LEVEL_TO_PLAYER_SIZE: { + if (newValue == null || newValue !== oldValue) { + const capLevelToPlayerSize = this.capLevelToPlayerSize; + if (this._hls) { + this._hls.config.capLevelToPlayerSize = capLevelToPlayerSize ?? false; + } + } + break; + }*/ } } diff --git a/packages/mux-video/src/react.ts b/packages/mux-video/src/react.ts index a320360bf..46b95474e 100644 --- a/packages/mux-video/src/react.ts +++ b/packages/mux-video/src/react.ts @@ -20,6 +20,7 @@ const ReactPropToAttrNameMap: Record = { playsInline: 'playsinline', disablePictureInPicture: 'disablepictureinpicture', disableRemotePlayback: 'disableremoteplayback', + capLevelToPlayerSize: 'cap-level-to-player-size', }; function toAttributeName(propName: string) { diff --git a/packages/playback-core/src/index.ts b/packages/playback-core/src/index.ts index 0b2a6b97e..15f58c6b8 100644 --- a/packages/playback-core/src/index.ts +++ b/packages/playback-core/src/index.ts @@ -2,7 +2,7 @@ import type { ValueOf, PlaybackCore, MuxMediaProps, MuxMediaPropsInternal, MuxMe import mux, { ErrorEvent } from 'mux-embed'; import Hls from './hls'; import type { HlsInterface } from './hls'; -import type { ErrorData, HlsConfig } from 'hls.js'; +import type { CapLevelController, ErrorData, HlsConfig } from 'hls.js'; import { MediaError, MuxErrorCategory, MuxErrorCode, errorCategoryToTokenNameOrPrefix } from './errors'; import { setupAutoplay } from './autoplay'; import { setupPreload } from './preload'; @@ -705,6 +705,7 @@ export const setupHls = ( | 'tokens' | 'drmTypeCb' | 'maxAutoResolution' + | 'capLevelToPlayerSize' > >, mediaEl: HTMLMediaElement @@ -743,8 +744,20 @@ export const setupHls = ( } : undefined; - const capLevelControllerObj = - _hlsConfig.capLevelToPlayerSize == null ? { capLevelController: MinCapLevelController } : {}; + let capLevelControllerObj : { + capLevelController?: typeof CapLevelController; + capLevelToPlayerSize?: boolean; + } = { capLevelController: undefined }; + if (_hlsConfig.capLevelToPlayerSize == null && !props.capLevelToPlayerSize) { + capLevelControllerObj = { + capLevelController: MinCapLevelController, + }; + } + if (props.capLevelToPlayerSize !== undefined) { + capLevelControllerObj = { + capLevelToPlayerSize: props.capLevelToPlayerSize, + }; + } const hls = new Hls({ // Kind of like preload metadata, but causes spinner. @@ -763,8 +776,8 @@ export const setupHls = ( xhr.open('GET', urlObj); }, - ...capLevelControllerObj, ...defaultConfig, + ...capLevelControllerObj, ...streamTypeConfig, ...drmConfig, ..._hlsConfig, diff --git a/packages/playback-core/src/types.ts b/packages/playback-core/src/types.ts index e0612b08c..1e53c80e3 100644 --- a/packages/playback-core/src/types.ts +++ b/packages/playback-core/src/types.ts @@ -174,6 +174,8 @@ export type MuxMediaPropTypes = { _hlsConfig?: Partial; autoPlay?: Autoplay; autoplay?: Autoplay; + capLevelToPlayerSize?: boolean; + minMaxCapResoultion?: number; beaconCollectionDomain: Options['beaconCollectionDomain']; customDomain: string; debug: Options['debug'] & Hls['config']['debug']; From 556bf6c50e145948f15c2a7c1818ab7c77b75938 Mon Sep 17 00:00:00 2001 From: Santiago Puppo Date: Wed, 3 Dec 2025 13:12:08 -0300 Subject: [PATCH 2/6] Cleaned up --- packages/playback-core/src/types.ts | 1 - 1 file changed, 1 deletion(-) diff --git a/packages/playback-core/src/types.ts b/packages/playback-core/src/types.ts index 1e53c80e3..16231a0cf 100644 --- a/packages/playback-core/src/types.ts +++ b/packages/playback-core/src/types.ts @@ -175,7 +175,6 @@ export type MuxMediaPropTypes = { autoPlay?: Autoplay; autoplay?: Autoplay; capLevelToPlayerSize?: boolean; - minMaxCapResoultion?: number; beaconCollectionDomain: Options['beaconCollectionDomain']; customDomain: string; debug: Options['debug'] & Hls['config']['debug']; From 3a6980c4f412d88169c17913663e974d1cbec16f Mon Sep 17 00:00:00 2001 From: Santiago Puppo Date: Thu, 4 Dec 2025 13:18:57 -0300 Subject: [PATCH 3/6] Cleaned up examples --- .../components/renderers.tsx | 56 +++++++++++++++++++ .../pages/mux-video-react.tsx | 35 ++++++++---- examples/vanilla-ts-esm/public/mux-video.html | 2 - 3 files changed, 79 insertions(+), 14 deletions(-) diff --git a/examples/nextjs-with-typescript/components/renderers.tsx b/examples/nextjs-with-typescript/components/renderers.tsx index e106474a4..71b0e9ec6 100644 --- a/examples/nextjs-with-typescript/components/renderers.tsx +++ b/examples/nextjs-with-typescript/components/renderers.tsx @@ -44,6 +44,62 @@ export const BooleanRenderer = ({ ); }; +export const OptionalBooleanRenderer = ({ + name, + value, + label, + onChange, + formatter = DefaultEnumFormatter +}: { + name: string; + value: boolean | undefined; + label?: string; + removeFalse?: boolean; + onChange: (obj: any) => void; + formatter?: (enumValue: boolean) => ReactNode; +}) => { + const labelStr = label ?? toWordsFromKeyName(name); + const values = [true, false]; + return ( +
+ +
+ { + + console.log("Selecting value:", undefined, toChangeObject(name, undefined)); + onChange(toChangeObject(name, undefined))}} + value="" + checked={value === undefined} + /> + + {values.map((enumValue, i) => { + return ( + + { + const changeValue = enumValue; + console.log("Selecting value:", changeValue, toChangeObject(name, changeValue)); + onChange(toChangeObject(name, changeValue)); + }} + value={enumValue.toString()} + checked={value === enumValue} + /> + + + ); + })} +
+
+ ); +}; + export const NumberRenderer = ({ name, value, diff --git a/examples/nextjs-with-typescript/pages/mux-video-react.tsx b/examples/nextjs-with-typescript/pages/mux-video-react.tsx index 205877d88..09ec6fe48 100644 --- a/examples/nextjs-with-typescript/pages/mux-video-react.tsx +++ b/examples/nextjs-with-typescript/pages/mux-video-react.tsx @@ -1,15 +1,20 @@ import Head from 'next/head'; import { useRef, useState } from "react"; import MuxVideo from "@mux/mux-video/react"; +import MuxPlayerElement from '@mux/mux-player'; +import { EnumRenderer, OptionalBooleanRenderer } from '../components/renderers'; +import MuxVideoElement from '@mux/mux-video'; const INITIAL_AUTOPLAY = false; const INITIAL_MUTED = false; -const INITIAL_CAP_LEVEL_TO_PLAYER_SIZE = false; +const INITIAL_CAP_LEVEL_TO_PLAYER_SIZE = undefined; +const INITIAL_PREFER_PLAYBACK = undefined; function MuxVideoPage() { - const mediaElRef = useRef(null); + const mediaElRef = useRef(null); const [autoplay, setAutoplay] = useState<"muted" | boolean>(INITIAL_AUTOPLAY); const [muted, setMuted] = useState(INITIAL_MUTED); + const [preferPlayback, setPreferPlayback] = useState(INITIAL_PREFER_PLAYBACK); const [capLevelToPlayerSize, setCapLevelToPlayerSize] = useState(INITIAL_CAP_LEVEL_TO_PLAYER_SIZE); const [paused, setPaused] = useState(true); @@ -40,7 +45,7 @@ function MuxVideoPage() { maxResolution="2160p" minResolution="540p" renditionOrder="desc" - preferPlayback="mse" + preferPlayback={preferPlayback} onPlay={() => { setPaused(false); }} @@ -50,6 +55,10 @@ function MuxVideoPage() { />
+
-
- - setCapLevelToPlayerSize(!capLevelToPlayerSize)} - checked={capLevelToPlayerSize} - /> -
+ setPreferPlayback(preferPlayback as MuxPlayerElement["preferPlayback"])} + values={['mse', 'native']} + /> + setCapLevelToPlayerSize(capLevelToPlayerSize)} + />
); diff --git a/examples/vanilla-ts-esm/public/mux-video.html b/examples/vanilla-ts-esm/public/mux-video.html index 5bd4df716..ac0fa81d0 100644 --- a/examples/vanilla-ts-esm/public/mux-video.html +++ b/examples/vanilla-ts-esm/public/mux-video.html @@ -40,8 +40,6 @@

Elements

metadata-video-title="Star Wars: Episode 3" metadata-viewer-user-id="user-id-6789" stream-type="on-demand" - cap-level-to-player-size="false" - prefer-playback="mse" controls muted > From 3e538896db5aa824bb4c754d22a56db96d9fe2b5 Mon Sep 17 00:00:00 2001 From: Santiago Puppo Date: Mon, 8 Dec 2025 18:55:03 -0300 Subject: [PATCH 4/6] Added property to mux-player-react and extended example --- .../pages/MuxPlayer.tsx | 5 ++++ packages/mux-player-react/src/types.ts | 1 + packages/playback-core/src/index.ts | 23 +++++++++---------- 3 files changed, 17 insertions(+), 12 deletions(-) diff --git a/examples/nextjs-with-typescript/pages/MuxPlayer.tsx b/examples/nextjs-with-typescript/pages/MuxPlayer.tsx index 16e8ac476..ec736c830 100644 --- a/examples/nextjs-with-typescript/pages/MuxPlayer.tsx +++ b/examples/nextjs-with-typescript/pages/MuxPlayer.tsx @@ -654,6 +654,11 @@ function MuxPlayerPage({ location }: Props) { min={0} step={1} /> + diff --git a/packages/mux-player-react/src/types.ts b/packages/mux-player-react/src/types.ts index 102e7b6ce..83ca28159 100644 --- a/packages/mux-player-react/src/types.ts +++ b/packages/mux-player-react/src/types.ts @@ -118,6 +118,7 @@ export type MuxPlayerProps = { theme?: string; themeProps?: { [k: string]: any }; fullscreenElement?: string; + capLevelToPlayerSize?: boolean; onAbort?: GenericEventListener; onCanPlay?: GenericEventListener; onCanPlayThrough?: GenericEventListener; diff --git a/packages/playback-core/src/index.ts b/packages/playback-core/src/index.ts index 15f58c6b8..80578185f 100644 --- a/packages/playback-core/src/index.ts +++ b/packages/playback-core/src/index.ts @@ -729,7 +729,6 @@ export const setupHls = ( backBufferLength: 30, renderTextTracksNatively: false, liveDurationInfinity: true, - capLevelToPlayerSize: true, capLevelOnFPSDrop: true, }; const streamTypeConfig = getStreamTypeConfig(streamType); @@ -744,19 +743,19 @@ export const setupHls = ( } : undefined; - let capLevelControllerObj : { + const capLevelControllerObj: { capLevelController?: typeof CapLevelController; capLevelToPlayerSize?: boolean; - } = { capLevelController: undefined }; - if (_hlsConfig.capLevelToPlayerSize == null && !props.capLevelToPlayerSize) { - capLevelControllerObj = { - capLevelController: MinCapLevelController, - }; - } - if (props.capLevelToPlayerSize !== undefined) { - capLevelControllerObj = { - capLevelToPlayerSize: props.capLevelToPlayerSize, - }; + } = {}; + + // If capLevelToPlayerSize is not explicitly set we enable MinCapLevelController + if (_hlsConfig.capLevelToPlayerSize == null && props.capLevelToPlayerSize == null) { + capLevelControllerObj.capLevelController = MinCapLevelController; + capLevelControllerObj.capLevelToPlayerSize = true; + } else { + capLevelControllerObj.capLevelController = undefined; + // hlsConfig will take precedence over props + capLevelControllerObj.capLevelToPlayerSize = props.capLevelToPlayerSize; } const hls = new Hls({ From 4099d8e587fcf593f6af03279dd600ab8d1df5f7 Mon Sep 17 00:00:00 2001 From: Santiago Puppo Date: Tue, 9 Dec 2025 10:55:30 -0300 Subject: [PATCH 5/6] Removed attribute support for capLevelToPlayerSize --- packages/mux-player/src/base.ts | 16 +++++++--------- packages/mux-video/src/base.ts | 14 +++++--------- 2 files changed, 12 insertions(+), 18 deletions(-) diff --git a/packages/mux-player/src/base.ts b/packages/mux-player/src/base.ts index 60677566d..3b09fd404 100644 --- a/packages/mux-player/src/base.ts +++ b/packages/mux-player/src/base.ts @@ -92,7 +92,6 @@ const PlayerAttributes = { NO_TOOLTIPS: 'no-tooltips', PROUDLY_DISPLAY_MUX_BADGE: 'proudly-display-mux-badge', DISABLE_PSEUDO_ENDED: 'disable-pseudo-ended', - CAP_LEVEL_TO_PLAYER_SIZE: 'cap-level-to-player-size', } as const; const ThemeAttributeNames = [ @@ -1886,16 +1885,15 @@ class MuxPlayerElement extends VideoApiElement implements IMuxPlayerElement { } } - get capLevelToPlayerSize() { - return this.hasAttribute(PlayerAttributes.CAP_LEVEL_TO_PLAYER_SIZE); + get capLevelToPlayerSize(): boolean | undefined { + if (this._hls) { + return this._hls.capLevelToPlayerSize; + } + return this._hlsConfig?.capLevelToPlayerSize; } - set capLevelToPlayerSize(val: boolean) { - if (!val) { - this.removeAttribute(PlayerAttributes.CAP_LEVEL_TO_PLAYER_SIZE); - return; - } - this.setAttribute(PlayerAttributes.CAP_LEVEL_TO_PLAYER_SIZE, ''); + set capLevelToPlayerSize(val: boolean | undefined) { + this._hlsConfig = { ...this._hlsConfig, capLevelToPlayerSize: val }; } } diff --git a/packages/mux-video/src/base.ts b/packages/mux-video/src/base.ts index 09095bffb..8cbbf7652 100644 --- a/packages/mux-video/src/base.ts +++ b/packages/mux-video/src/base.ts @@ -54,7 +54,6 @@ export * from './types.js'; export const Attributes = { BEACON_COLLECTION_DOMAIN: 'beacon-collection-domain', CUSTOM_DOMAIN: 'custom-domain', - CAP_LEVEL_TO_PLAYER_SIZE: 'cap-level-to-player-size', DEBUG: 'debug', DISABLE_TRACKING: 'disable-tracking', DISABLE_COOKIES: 'disable-cookies', @@ -518,17 +517,14 @@ export class MuxVideoBaseElement extends CustomVideoElement implements IMuxVideo } get capLevelToPlayerSize(): boolean | undefined { - let val = this.getAttribute(Attributes.CAP_LEVEL_TO_PLAYER_SIZE); - if (val == null) return undefined; - return val === 'true'; + if (this._hls) { + return this._hls.capLevelToPlayerSize; + } + return this._hlsConfig?.capLevelToPlayerSize; } set capLevelToPlayerSize(val: boolean | undefined) { - if (val !== undefined) { - this.setAttribute(Attributes.CAP_LEVEL_TO_PLAYER_SIZE, val.toString()); - } else { - this.removeAttribute(Attributes.CAP_LEVEL_TO_PLAYER_SIZE); - } + this._hlsConfig = { ...this._hlsConfig, capLevelToPlayerSize: val }; } get drmToken() { From 4cccc02d558c455bf1459e1c57bc816c235a9237 Mon Sep 17 00:00:00 2001 From: Santiago Puppo Date: Thu, 11 Dec 2025 14:55:35 -0300 Subject: [PATCH 6/6] Added disable cap level option --- packages/mux-player/src/base.ts | 14 +++++++++++++- packages/mux-video/src/base.ts | 13 +++++++++++++ packages/playback-core/src/index.ts | 18 ++++++++++++++---- packages/playback-core/src/types.ts | 1 + 4 files changed, 41 insertions(+), 5 deletions(-) diff --git a/packages/mux-player/src/base.ts b/packages/mux-player/src/base.ts index 3b09fd404..ba9f23a93 100644 --- a/packages/mux-player/src/base.ts +++ b/packages/mux-player/src/base.ts @@ -188,7 +188,7 @@ function getProps(el: MuxPlayerElement, state?: any): MuxTemplateProps { proudlyDisplayMuxBadge: el.hasAttribute(PlayerAttributes.PROUDLY_DISPLAY_MUX_BADGE), castReceiver: el.castReceiver, disablePseudoEnded: el.hasAttribute(PlayerAttributes.DISABLE_PSEUDO_ENDED), - capLevelToPlayerSize: el.hasAttribute(PlayerAttributes.CAP_LEVEL_TO_PLAYER_SIZE), + disableCapLevelToPlayerSize: el.disableCapLevelToPlayerSize, ...state, // NOTE: since the attribute value is used as the "source of truth" for the property getter, // moving this below the `...state` spread so it resolves to the default value when unset (CJP) @@ -1895,6 +1895,18 @@ class MuxPlayerElement extends VideoApiElement implements IMuxPlayerElement { set capLevelToPlayerSize(val: boolean | undefined) { this._hlsConfig = { ...this._hlsConfig, capLevelToPlayerSize: val }; } + + get disableCapLevelToPlayerSize(): boolean { + return this.hasAttribute(MuxVideoAttributes.DISABLE_CAP_LEVEL_TO_PLAYER_SIZE); + } + + set disableCapLevelToPlayerSize(val: boolean | undefined) { + if (!val) { + this.removeAttribute(MuxVideoAttributes.DISABLE_CAP_LEVEL_TO_PLAYER_SIZE); + } else { + this.setAttribute(MuxVideoAttributes.DISABLE_CAP_LEVEL_TO_PLAYER_SIZE, ''); + } + } } export function getVideoAttribute(el: MuxPlayerElement, name: string) { diff --git a/packages/mux-video/src/base.ts b/packages/mux-video/src/base.ts index 8cbbf7652..cd5fca7f3 100644 --- a/packages/mux-video/src/base.ts +++ b/packages/mux-video/src/base.ts @@ -82,6 +82,7 @@ export const Attributes = { LIVE_EDGE_OFFSET: 'live-edge-offset', TYPE: 'type', LOGO: 'logo', + DISABLE_CAP_LEVEL_TO_PLAYER_SIZE: 'disable-cap-level-to-player-size', } as const; const AttributeNameValues = Object.values(Attributes); @@ -527,6 +528,18 @@ export class MuxVideoBaseElement extends CustomVideoElement implements IMuxVideo this._hlsConfig = { ...this._hlsConfig, capLevelToPlayerSize: val }; } + get disableCapLevelToPlayerSize(): boolean { + return this.hasAttribute(Attributes.DISABLE_CAP_LEVEL_TO_PLAYER_SIZE); + } + + set disableCapLevelToPlayerSize(val: boolean | undefined) { + if (!val) { + this.removeAttribute(Attributes.DISABLE_CAP_LEVEL_TO_PLAYER_SIZE); + } else { + this.setAttribute(Attributes.DISABLE_CAP_LEVEL_TO_PLAYER_SIZE, ''); + } + } + get drmToken() { return this.getAttribute(Attributes.DRM_TOKEN) ?? undefined; } diff --git a/packages/playback-core/src/index.ts b/packages/playback-core/src/index.ts index 80578185f..062837bc9 100644 --- a/packages/playback-core/src/index.ts +++ b/packages/playback-core/src/index.ts @@ -705,7 +705,7 @@ export const setupHls = ( | 'tokens' | 'drmTypeCb' | 'maxAutoResolution' - | 'capLevelToPlayerSize' + | 'disableCapLevelToPlayerSize' > >, mediaEl: HTMLMediaElement @@ -749,15 +749,19 @@ export const setupHls = ( } = {}; // If capLevelToPlayerSize is not explicitly set we enable MinCapLevelController - if (_hlsConfig.capLevelToPlayerSize == null && props.capLevelToPlayerSize == null) { + if (_hlsConfig.capLevelToPlayerSize == null) { capLevelControllerObj.capLevelController = MinCapLevelController; capLevelControllerObj.capLevelToPlayerSize = true; } else { capLevelControllerObj.capLevelController = undefined; - // hlsConfig will take precedence over props - capLevelControllerObj.capLevelToPlayerSize = props.capLevelToPlayerSize; + capLevelControllerObj.capLevelToPlayerSize = _hlsConfig.capLevelToPlayerSize; + } + if (props.disableCapLevelToPlayerSize) { + capLevelControllerObj.capLevelController = undefined; + capLevelControllerObj.capLevelToPlayerSize = false; } + const hls = new Hls({ // Kind of like preload metadata, but causes spinner. // autoStartLoad: false, @@ -782,6 +786,12 @@ export const setupHls = ( ..._hlsConfig, }) as HlsInterface; + console.log("capLevelToPlayerSize summary", { + hlsConfigCapLevelToPlayerSize: _hlsConfig.capLevelToPlayerSize, + disableCapLevelToPlayerSize: props.disableCapLevelToPlayerSize, + capLevelControllerObj: capLevelControllerObj, + hlsCapLevelToPlayerSize: hls.capLevelToPlayerSize, + }); if (capLevelControllerObj.capLevelController === MinCapLevelController) { if (maxAutoResolution !== undefined) { MinCapLevelController.setMaxAutoResolution(hls, maxAutoResolution); diff --git a/packages/playback-core/src/types.ts b/packages/playback-core/src/types.ts index 16231a0cf..9428bb410 100644 --- a/packages/playback-core/src/types.ts +++ b/packages/playback-core/src/types.ts @@ -175,6 +175,7 @@ export type MuxMediaPropTypes = { autoPlay?: Autoplay; autoplay?: Autoplay; capLevelToPlayerSize?: boolean; + disableCapLevelToPlayerSize?: boolean; beaconCollectionDomain: Options['beaconCollectionDomain']; customDomain: string; debug: Options['debug'] & Hls['config']['debug'];