I-Frame variants (HLS #EXT-X-I-FRAME-STREAM-INF) can be used to load video I-Frames into a secondary HTMLVideoElement. This is useful for synchronized frame rendering.
To use them, call hls.createIFramePlayer() which returns an HlsIFramesOnly instance. This instance uses the current HLS instance's iframeVariants as its levels.
Key behaviors:
- I-Frame instances do not respond to external seeking or
currentTime changes on the attached element. You must use loadMediaAt(time) to buffer and seek. - The playlist selection is driven by the video element's dimensions if
capLevelToPlayerSize: true is set in the config. Ensure the element is sized before calling startLoad() or loadMediaAt(). - Audio in muxed segments is dropped; only video I-Frames are buffered.
- An I-Frame is considered appended on
FRAG_BUFFERED and rendered on the seeked event of the HTMLVideoElement.
const mainVideo = document.getElementById('video_1');
const iframeVideo = document.getElementById('video_2');
const hls = new Hls();
let hlsIframesOnly: HlsIFramesOnly | null = null;
hls.loadSource('http://example.com/primary.m3u8');
hls.attachMedia(mainVideo);
hls.once(Events.INIT_PTS_FOUND, createHlsIframesOnlyIfNeeded);
function createHlsIframesOnlyIfNeeded() {
if (hls.url !== hlsIframesOnly?.url) {
hlsIframesOnly = null;
}
if (!hlsIframesOnly && hls.iframeVariants.length) {
hlsIframesOnly = hls.createIFramePlayer();
if (hlsIframesOnly) {
hlsIframesOnly.attachMedia(iframeVideo);
hlsIframesOnly.startLoad();
hlsIframesOnly.once(
Events.LEVEL_UPDATED,
(name, { details: { fragments } }) => {
/* fragments contains all iframe start times and durations */
},
);
hlsIframesOnly.on(Events.FRAG_BUFFERED, (name, { frag }) => {
/* iframe buffered */
});
hlsIframesOnly.on(Events.ERROR, (name, { error }) => {
if (error.name == 'QuotaExceededError') {
/* MSE buffer is full */
}
});
}
}
}
function renderIFrame(currentTime) {
iframeVideo.onseeked = () => null;
hlsIframesOnly?.loadMediaAt(currentTime);
}