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 (
+
+ );
+};
+
export const NumberRenderer = ({
name,
value,
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/examples/nextjs-with-typescript/pages/mux-video-react.tsx b/examples/nextjs-with-typescript/pages/mux-video-react.tsx
index fd20dd3b9..09ec6fe48 100644
--- a/examples/nextjs-with-typescript/pages/mux-video-react.tsx
+++ b/examples/nextjs-with-typescript/pages/mux-video-react.tsx
@@ -1,14 +1,21 @@
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 = 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);
return (
@@ -32,12 +39,13 @@ function MuxVideoPage() {
// }}
// envKey="mux-data-env-key"
controls
+ capLevelToPlayerSize={capLevelToPlayerSize}
autoplay={autoplay}
muted={muted}
maxResolution="2160p"
minResolution="540p"
renditionOrder="desc"
- preferPlayback="native"
+ preferPlayback={preferPlayback}
onPlay={() => {
setPaused(false);
}}
@@ -47,6 +55,10 @@ function MuxVideoPage() {
/>
+
+
setPreferPlayback(preferPlayback as MuxPlayerElement["preferPlayback"])}
+ values={['mse', 'native']}
+ />
+ setCapLevelToPlayerSize(capLevelToPlayerSize)}
+ />
>
);
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/mux-player/src/base.ts b/packages/mux-player/src/base.ts
index fdafe58f7..ba9f23a93 100644
--- a/packages/mux-player/src/base.ts
+++ b/packages/mux-player/src/base.ts
@@ -188,6 +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),
+ 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)
@@ -1883,6 +1884,29 @@ class MuxPlayerElement extends VideoApiElement implements IMuxPlayerElement {
this.setAttribute(PlayerAttributes.PROUDLY_DISPLAY_MUX_BADGE, '');
}
}
+
+ get capLevelToPlayerSize(): boolean | undefined {
+ if (this._hls) {
+ return this._hls.capLevelToPlayerSize;
+ }
+ return this._hlsConfig?.capLevelToPlayerSize;
+ }
+
+ 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-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..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);
@@ -516,6 +517,29 @@ export class MuxVideoBaseElement extends CustomVideoElement implements IMuxVideo
}
}
+ get capLevelToPlayerSize(): boolean | undefined {
+ if (this._hls) {
+ return this._hls.capLevelToPlayerSize;
+ }
+ return this._hlsConfig?.capLevelToPlayerSize;
+ }
+
+ set capLevelToPlayerSize(val: boolean | undefined) {
+ 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;
}
@@ -900,6 +924,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..062837bc9 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'
+ | 'disableCapLevelToPlayerSize'
>
>,
mediaEl: HTMLMediaElement
@@ -728,7 +729,6 @@ export const setupHls = (
backBufferLength: 30,
renderTextTracksNatively: false,
liveDurationInfinity: true,
- capLevelToPlayerSize: true,
capLevelOnFPSDrop: true,
};
const streamTypeConfig = getStreamTypeConfig(streamType);
@@ -743,8 +743,24 @@ export const setupHls = (
}
: undefined;
- const capLevelControllerObj =
- _hlsConfig.capLevelToPlayerSize == null ? { capLevelController: MinCapLevelController } : {};
+ const capLevelControllerObj: {
+ capLevelController?: typeof CapLevelController;
+ capLevelToPlayerSize?: boolean;
+ } = {};
+
+ // If capLevelToPlayerSize is not explicitly set we enable MinCapLevelController
+ if (_hlsConfig.capLevelToPlayerSize == null) {
+ capLevelControllerObj.capLevelController = MinCapLevelController;
+ capLevelControllerObj.capLevelToPlayerSize = true;
+ } else {
+ capLevelControllerObj.capLevelController = undefined;
+ 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.
@@ -763,13 +779,19 @@ export const setupHls = (
xhr.open('GET', urlObj);
},
- ...capLevelControllerObj,
...defaultConfig,
+ ...capLevelControllerObj,
...streamTypeConfig,
...drmConfig,
..._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 e0612b08c..9428bb410 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;
+ disableCapLevelToPlayerSize?: boolean;
beaconCollectionDomain: Options['beaconCollectionDomain'];
customDomain: string;
debug: Options['debug'] & Hls['config']['debug'];