@@ -289,6 +289,7 @@ class MuxPlayerElement extends VideoApiElement implements IMuxPlayerElement {
289289 #isInit = false ;
290290 #tokens: Tokens = { } ;
291291 #userInactive = true ;
292+ #hasLoaded = false ;
292293 #hotkeys = new AttributeTokenList ( this , 'hotkeys' ) ;
293294 #state: Partial < MuxTemplateProps > = {
294295 ...initialState ,
@@ -378,29 +379,13 @@ class MuxPlayerElement extends VideoApiElement implements IMuxPlayerElement {
378379
379380 // NOTE: Make sure we re-render when stream type changes to ensure other props-driven
380381 // template details get updated appropriately (e.g. thumbnails track) (CJP)
381- this . media ?. addEventListener ( 'streamtypechange' , ( ) => {
382- // After stream type is determined from manifest, generate storyboard URL if needed
383- // This avoids Safari timing issues with dynamic URL generation during initial render
384- if (
385- this . streamType === StreamTypes . ON_DEMAND &&
386- this . preferPlayback === PlaybackTypes . MSE &&
387- ! this . #cachedStoryboardUrl
388- ) {
389- const { tokens } = this ;
390- if ( ! this . audio && this . playbackId && ( ! tokens . playback || tokens . storyboard ) ) {
391- this . #cachedStoryboardUrl = getStoryboardURLFromPlaybackId ( this . playbackId , {
392- customDomain : this . customDomain ,
393- token : tokens . storyboard ,
394- programStartTime : this . programStartTime ,
395- programEndTime : this . programEndTime ,
396- } ) ;
397- }
398- }
399- this . #render( ) ;
400- } ) ;
382+ this . media ?. addEventListener ( 'streamtypechange' , ( ) => this . #render( ) ) ;
401383
402384 // NOTE: Make sure we re-render when <source> tags are appended so hasSrc is updated.
403- this . media ?. addEventListener ( 'loadstart' , ( ) => this . #render( ) ) ;
385+ this . media ?. addEventListener ( 'loadstart' , ( ) => {
386+ this . #hasLoaded = true ;
387+ this . #render( ) ;
388+ } ) ;
404389 }
405390
406391 #setupCSSProperties( ) {
@@ -1008,20 +993,19 @@ class MuxPlayerElement extends VideoApiElement implements IMuxPlayerElement {
1008993 }
1009994 }
1010995
1011- #cachedStoryboardUrl?: string ;
1012996 /**
1013997 * Return the storyboard URL when a playback ID or storyboard-src is provided,
1014998 * we aren't an audio player and the stream-type isn't live.
1015999 */
10161000 get storyboard ( ) {
1001+ // NOTE: We don't want to return a storyboard until the media has loaded,
1002+ // otherwise iOS Safari would stall playback.
1003+ if ( ! this . #hasLoaded) return undefined ;
1004+
10171005 const { tokens } = this ;
10181006 // If the storyboardSrc has been explicitly set, assume it should be used
10191007 if ( this . storyboardSrc && ! tokens . storyboard ) return this . storyboardSrc ;
10201008
1021- const isOnDemandMse = this . streamType === StreamTypes . ON_DEMAND && this . preferPlayback === PlaybackTypes . MSE ;
1022- if ( isOnDemandMse ) {
1023- return this . #cachedStoryboardUrl;
1024- }
10251009 if (
10261010 // NOTE: Some audio use cases may have a storyboard (e.g. it's an audio+video stream being played *as* audio)
10271011 // Consider supporting cases (CJP)
0 commit comments