-
Notifications
You must be signed in to change notification settings - Fork 2
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
DE-6967: HLS Interstitial Player Component
- Loading branch information
1 parent
f0be44f
commit 2a320b9
Showing
12 changed files
with
801 additions
and
1 deletion.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,46 @@ | ||
import { clpp } from '@castlabs/prestoplay' | ||
import React from 'react' | ||
|
||
import { InterstitialPlayer } from '../../src' | ||
|
||
/** | ||
* A page featuring the HLS interstitial player. | ||
*/ | ||
export const InterstitialPage = () => { | ||
return ( | ||
<main className="in-page"> | ||
<div className="in-container"> | ||
<div className="in-video-container"> | ||
<InterstitialPlayer | ||
asset={{ | ||
source: { | ||
// url: 'http://localhost:3000/vod-fixed.m3u8', | ||
url: 'http://localhost:3000/vod-preroll.m3u8', | ||
type: clpp.Type.HLS, | ||
}, | ||
}} | ||
interstitialOptions={{ | ||
// Start resolving X-ASSET-LIST 15 seconds or less before | ||
// the cue is scheduled | ||
resolutionOffsetSec: 15, | ||
}} | ||
// onPlayerChanged={p => { | ||
// // @ts-ignore | ||
// window.player = p | ||
// }} | ||
// showInterstitialMarkers={false} | ||
// seekStep={2} | ||
// controlsVisibility='never' | ||
// intermissionDuration={5} | ||
// interstitialLabel={(i) => `Ad ${i.podOrder} of ${i.podCount}`} | ||
// renderInterstitialLabel={(i) => null} | ||
// renderIntermission={(seconds) => <div>{seconds}</div>} | ||
// loop={false} | ||
// onEnded={() => {}} | ||
// onLoopEnded={() => {}} | ||
/> | ||
</div> | ||
</div> | ||
</main> | ||
) | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,159 @@ | ||
import { clpp } from '@castlabs/prestoplay' | ||
import '@castlabs/prestoplay/cl.mse' | ||
import '@castlabs/prestoplay/cl.hls' | ||
import React, { useEffect, useRef } from 'react' | ||
|
||
import { ControlsVisibilityMode } from '../services/controls' | ||
|
||
import { InterstitialOverlay } from './components/OverlayHlsi' | ||
import { PlayerSurfaceHlsi } from './components/PlayerSurfaceHlsi' | ||
import { PlayerHlsi } from './PlayerHlsi' | ||
import { HlsInterstitial } from './types' | ||
|
||
// eslint-disable-next-line @typescript-eslint/ban-ts-comment | ||
// @ts-ignore | ||
clpp.install(clpp.hls.HlsComponent) | ||
|
||
export type InterstitialPlayerProps = { | ||
/** | ||
* HLS interstitial Asset | ||
*/ | ||
asset: clpp.PlayerConfiguration | ||
/** | ||
* If the asset should be played back in a loop, one cycle of the loop | ||
* consists of a few seconds of intermission and then the asset is played. | ||
* Default: true. | ||
*/ | ||
loop?: boolean | ||
/** | ||
* Intermission duration in seconds, defaults to 3. | ||
*/ | ||
intermissionDuration?: number | null | ||
/** | ||
* Intermission element renderer. Default: Countdown | ||
*/ | ||
renderIntermission?: (seconds: number) => (JSX.Element | null) | ||
/** | ||
* Callback called when playback ended (if loop is `false`) | ||
*/ | ||
onEnded?: () => any | ||
/** | ||
* Callback called when one loop cycle ended (if loop is `true`) | ||
*/ | ||
onLoopEnded?: () => any | ||
/** | ||
* Interstitial label text renderer. Default: `Interstitial ${podOrder} of ${podCount}` | ||
*/ | ||
interstitialLabel?: (i: HlsInterstitial) => string | ||
/** | ||
* Interstitial label component renderer. | ||
*/ | ||
renderInterstitialLabel?: (i: HlsInterstitial) => (JSX.Element | null) | ||
/** | ||
* Visibility mode of UI controls. Default: 'always-visible' | ||
*/ | ||
controlsVisibility?: ControlsVisibilityMode | ||
/** | ||
* Seek step in seconds for seek buttons. A value of 0 will hide the buttons. | ||
* Default: 10. | ||
*/ | ||
seekStep?: number | ||
/** | ||
* If true interstitial markers should be shown on the timeline. Default: true. | ||
*/ | ||
showInterstitialMarkers?: boolean | ||
/** | ||
* If true, a fullscreen button is displayed. Defaults to true. | ||
*/ | ||
hasFullScreenButton?: boolean | ||
/** | ||
* If true, audio controls are displayed. Defaults to false. | ||
*/ | ||
hasAudioControls?: boolean | ||
/** | ||
* If true, track controls are displayed. Defaults to false. | ||
*/ | ||
hasTrackControls?: boolean | ||
/** | ||
* Callback called when the player of multi-controller changes. | ||
*/ | ||
onPlayerChanged?: (p: clpp.Player) => any | ||
/** | ||
* Options for clpp.interstitial.Player | ||
*/ | ||
interstitialOptions?: Omit<clpp.interstitial.Options, 'anchorEl'> | ||
/** | ||
* Custom class name for the player container. | ||
*/ | ||
className?: string | ||
/** | ||
* Custom style for the player container. | ||
*/ | ||
style?: React.CSSProperties | ||
} | ||
|
||
/** | ||
* Interstitial player component. | ||
*/ | ||
export const InterstitialPlayer = React.memo((props: InterstitialPlayerProps) => { | ||
const playerRef = useRef(new PlayerHlsi()) | ||
|
||
useEffect(() => { | ||
// Possibly it's something wrong with the AIP stream http://localhost:3000/vod-preroll.m3u8 | ||
// but unfortunately what happens is that we get state "Ended" and then the video | ||
// continues playing for another cca 800ms. This would obviously cause a glitch | ||
// in the UI so configure the player to ignore all ended states changes | ||
playerRef.current.ignoreStateEnded = true | ||
}, []) | ||
|
||
const load = async () => { | ||
try { | ||
await playerRef.current.loadHlsi(props.asset) | ||
} catch (e) { | ||
console.error('Interstitial player Failed to load asset', e) | ||
} | ||
} | ||
|
||
useEffect(() => { | ||
if (props.onPlayerChanged) { | ||
playerRef.current.onUIEvent('playerChanged', props.onPlayerChanged) | ||
} | ||
|
||
return () => { | ||
if (props.onPlayerChanged) { | ||
playerRef.current.offUIEvent('playerChanged', props.onPlayerChanged) | ||
} | ||
} | ||
}, []) | ||
|
||
let className = 'pp-ui-hlsi-player' | ||
if (props.className) { | ||
className += ` ${props.className}` | ||
} | ||
|
||
return ( | ||
<div className={className} style={props.style}> | ||
<PlayerSurfaceHlsi | ||
player={playerRef.current} | ||
interstitialOptions={props.interstitialOptions} | ||
> | ||
<InterstitialOverlay | ||
{...props} | ||
onStartClick={async () => { | ||
await load() | ||
}} | ||
onLoopEnded={async () => { | ||
props.onLoopEnded?.() | ||
await playerRef.current.reset() | ||
await load() | ||
}} | ||
onIntermissionEnded={async () => { | ||
await playerRef.current.unpause() | ||
}} | ||
/> | ||
</PlayerSurfaceHlsi> | ||
</div> | ||
) | ||
}) | ||
|
||
InterstitialPlayer.displayName = 'InterstitialPlayer' |
Oops, something went wrong.