diff --git a/src/components/ha-hls-player.ts b/src/components/ha-hls-player.ts index 5b84c8d4e2..05e7e6cdc9 100644 --- a/src/components/ha-hls-player.ts +++ b/src/components/ha-hls-player.ts @@ -104,7 +104,6 @@ class HaHLSPlayer extends LitElement { private async _startHls(): Promise { const videoEl = this._videoEl; - const playlist_url = this.url.replace("master_playlist", "playlist"); const useExoPlayerPromise = this._getUseExoPlayer(); const masterPlaylistPromise = fetch(this.url); @@ -126,13 +125,30 @@ class HaHLSPlayer extends LitElement { } this._useExoPlayer = await useExoPlayerPromise; - let hevcRegexp: RegExp; - let masterPlaylist: string; - if (this._useExoPlayer) { - hevcRegexp = /CODECS=".*?((hev1)|(hvc1))\..*?"/; - masterPlaylist = await (await masterPlaylistPromise).text(); + const masterPlaylist = await (await masterPlaylistPromise).text(); + + // Parse playlist assuming it is a master playlist. Match group 1 is whether hevc, match group 2 is regular playlist url + // See https://tools.ietf.org/html/rfc8216 for HLS spec details + const playlistRegexp = /#EXT-X-STREAM-INF:.*?(?:CODECS=".*?(?hev1|hvc1)?\..*?".*?)?(?:\n|\r\n)(?.+)/g; + const match = playlistRegexp.exec(masterPlaylist); + const matchTwice = playlistRegexp.exec(masterPlaylist); + + // Get the regular playlist url from the input (master) playlist, falling back to the input playlist if necessary + // This avoids the player having to load and parse the master playlist again before loading the regular playlist + let playlist_url: string; + if (match !== null && matchTwice === null) { + // Only send the regular playlist url if we match exactly once + playlist_url = new URL(match.groups!.streamUrl, this.url).href; + } else { + playlist_url = this.url; } - if (this._useExoPlayer && hevcRegexp!.test(masterPlaylist!)) { + + // If codec is HEVC and ExoPlayer is supported, use ExoPlayer. + if ( + this._useExoPlayer && + match !== null && + match.groups!.isHevc !== undefined + ) { this._renderHLSExoPlayer(playlist_url); } else if (hls.isSupported()) { this._renderHLSPolyfill(videoEl, hls, playlist_url);