diff --git a/examples/nextjs-with-typescript/pages/MuxPlayer.tsx b/examples/nextjs-with-typescript/pages/MuxPlayer.tsx index b7f5f1579..6bcf25e77 100644 --- a/examples/nextjs-with-typescript/pages/MuxPlayer.tsx +++ b/examples/nextjs-with-typescript/pages/MuxPlayer.tsx @@ -351,6 +351,11 @@ function MuxPlayerPage({ location }: Props) { theme={state.theme} envKey={state.envKey} metadata={state.metadata} + // Test _hlsConfig for MuxPlayer (react) (Note: This also indirectly tests & ) + // _hlsConfig={{ + // startLevel: 2, + // debug: true, + // }} title={state.title} startTime={state.startTime} currentTime={state.currentTime} diff --git a/examples/nextjs-with-typescript/pages/MuxVideo.tsx b/examples/nextjs-with-typescript/pages/MuxVideo.tsx index 9ee83d2d9..49c64ab8f 100644 --- a/examples/nextjs-with-typescript/pages/MuxVideo.tsx +++ b/examples/nextjs-with-typescript/pages/MuxVideo.tsx @@ -21,6 +21,11 @@ function MuxVideoPage() { >((props, ref) => { - const { playbackId, src: outerSrc, children, autoPlay, preload, ...restProps } = props; + const { playbackId, src: outerSrc, children, autoPlay, preload, _hlsConfig, ...restProps } = props; const [playerInitTime] = useState(generatePlayerInitTime()); const [src, setSrc] = useState(toMuxVideoURL(props) ?? outerSrc); @@ -82,6 +82,7 @@ MuxAudio.propTypes = { // Improve this by adding a full shape() definition for all metadata props // metadata: PropTypes.shape({}), metadata: PropTypes.any, + _hlsConfig: PropTypes.any, beaconCollectionDomain: PropTypes.string, playbackId: PropTypes.string, playerInitTime: PropTypes.number, diff --git a/packages/mux-audio/src/index.ts b/packages/mux-audio/src/index.ts index 6a96fe893..dcd3d099a 100644 --- a/packages/mux-audio/src/index.ts +++ b/packages/mux-audio/src/index.ts @@ -18,6 +18,7 @@ import type { PlaybackCore, PlaybackEngine, ExtensionMimeTypeMap } from '@mux/pl import { getPlayerVersion } from './env'; // this must be imported after playback-core for the polyfill to be included import { CustomAudioElement, Events as AudioEvents } from 'custom-media-element'; +import { HlsConfig } from 'hls.js'; /** @TODO make the relationship between name+value smarter and more deriveable (CJP) */ type AttributeNames = { @@ -60,6 +61,7 @@ class MuxAudioElement extends CustomAudioElement implements Partial | null; #playerInitTime: number; #metadata: Readonly = {}; + #_hlsConfig?: Partial; constructor() { super(); @@ -294,6 +296,14 @@ class MuxAudioElement extends CustomAudioElement implements Partial> | undefined) { + this.#_hlsConfig = val; + } + async #requestLoad() { if (this.#loadRequested) return; await (this.#loadRequested = Promise.resolve()); diff --git a/packages/mux-player-react/src/index.tsx b/packages/mux-player-react/src/index.tsx index 0f5cad5a0..78cc6f533 100644 --- a/packages/mux-player-react/src/index.tsx +++ b/packages/mux-player-react/src/index.tsx @@ -52,6 +52,7 @@ type MuxMediaPropTypes = { // metadata: Partial; metadata: { [k: string]: any }; extraSourceParams: Record; + _hlsConfig: MuxPlayerElement['_hlsConfig']; beaconCollectionDomain: string; customDomain: string; playbackId: string; @@ -182,11 +183,13 @@ const usePlayer = ( currentTime, themeProps, extraSourceParams, + _hlsConfig, ...remainingProps } = props; useObjectPropEffect('playbackRates', playbackRates, ref); useObjectPropEffect('metadata', metadata, ref); useObjectPropEffect('extraSourceParams', extraSourceParams, ref); + useObjectPropEffect('_hlsConfig', _hlsConfig, ref); useObjectPropEffect('themeProps', themeProps, ref); useObjectPropEffect('tokens', tokens, ref); useObjectPropEffect('playbackId', playbackId, ref); diff --git a/packages/mux-player/src/index.ts b/packages/mux-player/src/index.ts index f8b76586a..fdaabe857 100644 --- a/packages/mux-player/src/index.ts +++ b/packages/mux-player/src/index.ts @@ -37,6 +37,7 @@ import { toNumberOrUndefined, i18n, parseJwt, containsComposedNode, camelCase, k import * as logger from './logger'; import type { MuxTemplateProps, ErrorEvent } from './types'; import './themes/gerwig'; +import { HlsConfig } from 'hls.js'; const DefaultThemeName = 'gerwig'; export { MediaError }; @@ -531,6 +532,7 @@ class MuxPlayerElement extends VideoApiElement implements MuxPlayerElement { // - cues that are not at the bottom // - line is less than -5 // - line is between 0 and 10 + // @ts-ignore if (!cue.snapToLines || cue.line < -5 || (cue.line >= 0 && cue.line < 10)) { return; } @@ -1441,6 +1443,27 @@ class MuxPlayerElement extends VideoApiElement implements MuxPlayerElement { this.media.metadata = { ...getMetadataFromAttrs(this), ...val }; } + /** + * Get the metadata object for Mux Data. + */ + get _hlsConfig() { + return this.media?._hlsConfig; + } + + /** + * Set the metadata object for Mux Data. + */ + set _hlsConfig(val: Readonly> | undefined) { + this.#init(); + + // NOTE: This condition should never be met. If it is, there is a bug (CJP) + if (!this.media) { + logger.error('underlying media element missing when trying to set _hlsConfig. _hlsConfig will not be set.'); + return; + } + this.media._hlsConfig = val; + } + async addCuePoints(cuePoints: { time: number; value: T }[]) { this.#init(); diff --git a/packages/mux-video-react/src/index.tsx b/packages/mux-video-react/src/index.tsx index 44f3231e2..e12a429bd 100644 --- a/packages/mux-video-react/src/index.tsx +++ b/packages/mux-video-react/src/index.tsx @@ -24,7 +24,7 @@ const playerSoftwareVersion = getPlayerVersion(); const playerSoftwareName = 'mux-video-react'; const MuxVideo = React.forwardRef>((props, ref) => { - const { playbackId, src: outerSrc, children, autoPlay, preload, streamType, ...restProps } = props; + const { playbackId, src: outerSrc, children, autoPlay, preload, streamType, _hlsConfig, ...restProps } = props; const [playerInitTime] = useState(generatePlayerInitTime()); const [src, setSrc] = useState(toMuxVideoURL(props) ?? outerSrc); @@ -82,6 +82,7 @@ MuxVideo.propTypes = { // Improve this by adding a full shape() definition for all metadata props // metadata: PropTypes.shape({}), metadata: PropTypes.any, + _hlsConfig: PropTypes.any, beaconCollectionDomain: PropTypes.string, playbackId: PropTypes.string, playerInitTime: PropTypes.number, diff --git a/packages/mux-video/src/index.ts b/packages/mux-video/src/index.ts index 04d1bc94c..6978e78a3 100644 --- a/packages/mux-video/src/index.ts +++ b/packages/mux-video/src/index.ts @@ -39,6 +39,7 @@ import { getPlayerVersion } from './env'; import 'castable-video'; import { CustomMediaMixin, Events as VideoEvents } from 'custom-media-element'; import { MediaTracksMixin } from 'media-tracks'; +import type { HlsConfig } from 'hls.js'; // Must mutate so the added events are available in custom-media-element. VideoEvents.push('castchange', 'entercast', 'leavecast'); @@ -86,6 +87,7 @@ class MuxVideoElement extends CustomVideoElement implements Partial | null; #playerInitTime: number; #metadata: Readonly = {}; + #_hlsConfig?: Partial; #playerSoftwareVersion?: string; #playerSoftwareName?: string; #errorTranslator?: (errorEvent: any) => any; @@ -514,6 +516,14 @@ class MuxVideoElement extends CustomVideoElement implements Partial> | undefined) { + this.#_hlsConfig = val; + } + async #requestLoad() { if (this.#loadRequested) return; await (this.#loadRequested = Promise.resolve()); diff --git a/packages/playback-core/src/index.ts b/packages/playback-core/src/index.ts index ef0ca2808..f74dc69a0 100644 --- a/packages/playback-core/src/index.ts +++ b/packages/playback-core/src/index.ts @@ -424,11 +424,14 @@ function useNative( export const setupHls = ( props: Partial< - Pick + Pick< + MuxMediaPropsInternal, + 'debug' | 'streamType' | 'type' | 'startTime' | 'metadata' | 'preferCmcd' | '_hlsConfig' + > >, mediaEl: Pick ) => { - const { debug, streamType, startTime: startPosition = -1, metadata, preferCmcd } = props; + const { debug, streamType, startTime: startPosition = -1, metadata, preferCmcd, _hlsConfig = {} } = props; const type = getType(props); const hlsType = type === ExtensionMimeTypeMap.M3U8; const shouldUseNative = useNative(props, mediaEl); @@ -471,6 +474,7 @@ export const setupHls = ( }, ...defaultConfig, ...streamTypeConfig, + ..._hlsConfig, }) as HlsInterface; return hls; diff --git a/packages/playback-core/src/types.ts b/packages/playback-core/src/types.ts index 4807dd2b3..59b29517d 100644 --- a/packages/playback-core/src/types.ts +++ b/packages/playback-core/src/types.ts @@ -2,8 +2,9 @@ /// import type { Options } from 'mux-embed'; import type { MediaError } from './errors'; -import type { HlsInterface as Hls } from './hls'; import type { VideoTrack, AudioTrack, VideoTrackList, AudioTrackList } from 'media-tracks'; +import type { HlsConfig } from 'hls.js'; +import type Hls from 'hls.js'; type KeyTypes = string | number | symbol; type Maybe = T | null | undefined; @@ -174,6 +175,7 @@ export type MuxMediaPropTypes = { autoplay?: Autoplay; preferCmcd: ValueOf | undefined; error?: HTMLMediaElement['error'] | MediaError; + _hlsConfig?: Partial; }; export interface MediaTracks {