From 5906f3539e144434f9478d7f626cd996ccff667a Mon Sep 17 00:00:00 2001 From: Igor Zolotarenko Date: Fri, 7 Jul 2023 09:25:00 +0300 Subject: [PATCH 01/16] Add hls stream level control. --- p2p-media-loader-demo/src/App.tsx | 2 +- .../src/services/engine.ts | 14 +- .../src/services/fragment-loader.ts | 142 +++++++++++++++--- .../src/services/playlist.ts | 4 + .../src/services/segment-mananger.ts | 52 ++++++- 5 files changed, 186 insertions(+), 28 deletions(-) diff --git a/p2p-media-loader-demo/src/App.tsx b/p2p-media-loader-demo/src/App.tsx index 5b73992b..c3c46d60 100644 --- a/p2p-media-loader-demo/src/App.tsx +++ b/p2p-media-loader-demo/src/App.tsx @@ -182,7 +182,7 @@ function App() { }; const onVideoUrlChange = (url: string) => { - localStorage.url = url; + localStorage.videoUrl = url; setUrl(url); destroyAndWindowPlayer(); }; diff --git a/packages/p2p-media-loader-hlsjs/src/services/engine.ts b/packages/p2p-media-loader-hlsjs/src/services/engine.ts index df14a946..deb1d3cb 100644 --- a/packages/p2p-media-loader-hlsjs/src/services/engine.ts +++ b/packages/p2p-media-loader-hlsjs/src/services/engine.ts @@ -18,12 +18,24 @@ export class Engine { public initHlsJsEvents(hls: Hls) { hls.on("hlsManifestLoaded" as Events.MANIFEST_LOADED, (event, data) => { + console.log(data.levels); this.segmentManager.processMasterManifest(data); }); - hls.on("hlsLevelLoaded" as Events.LEVEL_LOADED, (event, data) => { + hls.on("hlsLevelLoaded" as Events.LEVEL_LOADING, (event, data) => { + console.log("LEVEL_LOADING", data); + // this.segmentManager.setPlaylist(data); + }); + + hls.on("hlsLevelUpdated" as Events.LEVEL_UPDATED, (event, data) => { + console.log("LEVEL_UPDATED", data); this.segmentManager.setPlaylist(data); }); + + hls.on("hlsLevelLoaded" as Events.LEVEL_LOADED, (event, data) => { + console.log("LEVEL_LOADED"); + // this.segmentManager.setPlaylist(data); + }); } private createFragmentLoaderClass() { diff --git a/packages/p2p-media-loader-hlsjs/src/services/fragment-loader.ts b/packages/p2p-media-loader-hlsjs/src/services/fragment-loader.ts index 8e43d366..ac62f6fb 100644 --- a/packages/p2p-media-loader-hlsjs/src/services/fragment-loader.ts +++ b/packages/p2p-media-loader-hlsjs/src/services/fragment-loader.ts @@ -1,16 +1,18 @@ import type { + FragmentLoaderContext, + HlsConfig, Loader, LoaderCallbacks, LoaderConfiguration, - LoaderStats, - FragmentLoaderContext, - HlsConfig, LoaderContext, + LoaderStats, } from "hls.js"; import { SegmentManager } from "./segment-mananger"; -import { Segment } from "./playlist"; +import { ByteRange, Segment } from "./playlist"; import Debug from "debug"; +let prev: string | undefined; + export class FragmentLoaderBase implements Loader { context!: FragmentLoaderContext; config!: LoaderConfiguration | null; @@ -18,12 +20,23 @@ export class FragmentLoaderBase implements Loader { stats: LoaderStats; defaultLoader: Loader; segmentManager: SegmentManager; - private debug = Debug("p2pml:fragment-loader"); + private debug = Debug("hls:fragment-loading"); constructor(config: HlsConfig, segmentManager: SegmentManager) { this.segmentManager = segmentManager; this.defaultLoader = new config.loader(config); this.stats = this.defaultLoader.stats; + // this.stats = { + // loading: { start: 0, end: 0, first: 0 }, + // total: 0, + // loaded: 0, + // bwEstimate: 0, + // buffering: { start: 0, end: 0, first: 0 }, + // aborted: false, + // retry: 0, + // parsing: { start: 0, end: 0 }, + // chunkCount: 0, + // }; } async load( @@ -34,25 +47,110 @@ export class FragmentLoaderBase implements Loader { this.context = context; this.config = config; this.callbacks = callbacks; - this.defaultLoader.load(context, config, { - ...callbacks, - onSuccess: (response, stats, context, networkDetails) => { - const { rangeStart: start, rangeEnd: end } = context; - const segmentId = Segment.getSegmentLocalId(context.url, { - start, - end: end !== undefined ? end - 1 : undefined, - }); - const playlist = this.segmentManager.getPlaylistBySegmentId(segmentId); - this.debug( - "downloaded segment from playlist\n", - `playlist v: ${playlist?.index}\n`, - `segment: `, - playlist?.segments.get(segmentId)?.index - ); - - return callbacks.onSuccess(response, stats, context, networkDetails); + const stats = this.stats; + + this.identifyPlaylist(context); + let byteRange: ByteRange | undefined; + const { rangeStart, rangeEnd } = context; + if ( + rangeStart !== undefined && + rangeEnd !== undefined && + rangeEnd > rangeStart + ) { + byteRange = { start: rangeStart, end: rangeEnd }; + } + const response = await this.fetchSegment(context.url, byteRange); + const { loading } = stats; + const loadedBytes = response.data.byteLength; + loading.first = performance.now(); + loading.end = performance.now() + 1; + + const { bandwidth, loadingStartTime } = + this.getLoadingStartByTargetBandwidth({ + targetLevelBandwidth: 460560, + aboveLevelBandwidth: 836280, + loadingEndTime: loading.first, + loadedBytes, + }); + + console.log("bandwidth", bandwidth); + loading.start = loadingStartTime; + stats.bwEstimate = bandwidth; + stats.total = stats.loaded = loadedBytes; + + callbacks.onSuccess( + { + url: response.url, + code: response.status, + data: response.data, }, + this.stats, + context, + response + ); + } + + private identifyPlaylist(context: LoaderContext) { + const { rangeStart: start, rangeEnd: end } = context; + const segmentId = Segment.getSegmentLocalId(context.url, { + start, + end, }); + const prevPlaylist = prev + ? this.segmentManager.getPlaylistBySegmentId(prev) + : undefined; + + const playlist = this.segmentManager.getPlaylistBySegmentId(segmentId); + prev = segmentId; + console.log(""); + console.log("PREV_PLAYLIST", prevPlaylist?.index); + console.log(context.url); + // console.log("SEGMENT_ID: ", segmentId); + console.log("PLAYLIST_INDEX: ", playlist?.index); + console.log("PLAYLIST_TYPE: ", playlist?.type); + this.debug( + "downloaded segment from playlist\n", + `playlist v: ${playlist?.index}\n`, + `segment: `, + playlist?.segments.get(segmentId)?.index, + `bitrate: ${playlist?.bitrate}` + ); + } + + getLoadingStartByTargetBandwidth({ + loadedBytes, + targetLevelBandwidth, + aboveLevelBandwidth, + loadingEndTime, + }: { + targetLevelBandwidth: number; + aboveLevelBandwidth: number; + loadingEndTime: number; + loadedBytes: number; + }) { + const bites = loadedBytes * 8; + const levelBandwidthDiff = aboveLevelBandwidth - targetLevelBandwidth; + const targetBandwidth = Math.round( + targetLevelBandwidth + levelBandwidthDiff * 0.4 + ); + const timeForLoading = Math.round((bites / targetBandwidth) * 1000); + const loadingStartTime = loadingEndTime - timeForLoading; + return { loadingStartTime, bandwidth: targetBandwidth }; + } + + async fetchSegment(segmentUrl: string, byteRange?: ByteRange) { + const headers = new Headers(); + + if (byteRange) { + const { start, end } = byteRange; + const byteRangeString = `bytes=${start}-${end}`; + headers.set("Range", byteRangeString); + } + const response = await fetch(segmentUrl, { headers }); + const data = await response.arrayBuffer(); + const { status, url } = response; + + return { status, data, url }; } abort() { diff --git a/packages/p2p-media-loader-hlsjs/src/services/playlist.ts b/packages/p2p-media-loader-hlsjs/src/services/playlist.ts index 520c6812..abff3aba 100644 --- a/packages/p2p-media-loader-hlsjs/src/services/playlist.ts +++ b/packages/p2p-media-loader-hlsjs/src/services/playlist.ts @@ -10,20 +10,24 @@ export class Playlist { id: string; index: number; type: SegmentType; + bitrate: number; segments: Map = new Map(); constructor({ masterManifestUrl, index, type, + bitrate, }: { masterManifestUrl: string; index: number; type: SegmentType; + bitrate: number; }) { this.index = index; this.type = type; this.id = `${getUrlWithoutParameters(masterManifestUrl)}-${type}-V${index}`; + this.bitrate = bitrate; } } diff --git a/packages/p2p-media-loader-hlsjs/src/services/segment-mananger.ts b/packages/p2p-media-loader-hlsjs/src/services/segment-mananger.ts index 8c5a32dd..598528c1 100644 --- a/packages/p2p-media-loader-hlsjs/src/services/segment-mananger.ts +++ b/packages/p2p-media-loader-hlsjs/src/services/segment-mananger.ts @@ -1,5 +1,9 @@ import { Playlist, Segment } from "./playlist"; -import type { LevelLoadedData, ManifestLoadedData } from "hls.js"; +import type { + LevelLoadedData, + ManifestLoadedData, + LevelUpdatedData, +} from "hls.js"; export class SegmentManager { isLive?: boolean; @@ -17,7 +21,12 @@ export class SegmentManager { if (this.playlists.has(level.url)) return; this.playlists.set( level.url, - new Playlist({ type: "video", index, masterManifestUrl: url }) + new Playlist({ + type: "video", + index, + masterManifestUrl: url, + bitrate: level.bitrate, + }) ); }); @@ -25,12 +34,47 @@ export class SegmentManager { if (this.playlists.has(track.url)) return; this.playlists.set( track.url, - new Playlist({ type: "audio", index, masterManifestUrl: url }) + new Playlist({ + type: "audio", + index, + masterManifestUrl: url, + bitrate: track.bitrate, + }) ); }); } - setPlaylist(data: LevelLoadedData) { + // setPlaylist(data: LevelLoadedData) { + // const { + // details: { url, fragments, live }, + // } = data; + // const playlist = this.playlists.get(url); + // if (!playlist) return; + // + // this.isLive = live; + // + // const segmentToRemoveIds = new Set(playlist.segments.keys()); + // fragments.forEach((fragment, index) => { + // const { url, byteRange, sn } = fragment; + // if (sn === "initSegment") return; + // + // const [start, end] = byteRange; + // const segmentLocalId = Segment.getSegmentLocalId(url, { start, end }); + // segmentToRemoveIds.delete(segmentLocalId); + // + // if (playlist.segments.has(segmentLocalId)) return; + // const segment = new Segment({ + // segmentUrl: url, + // index: live ? sn : index, + // ...(start && end ? { byteRange: { start, end } } : {}), + // }); + // playlist.segments.set(segment.localId, segment); + // }); + // + // segmentToRemoveIds.forEach((value) => playlist.segments.delete(value)); + // } + + setPlaylist(data: LevelUpdatedData) { const { details: { url, fragments, live }, } = data; From ef082b7c7613d6296ad893678b087abc7697b971 Mon Sep 17 00:00:00 2001 From: Igor Zolotarenko Date: Fri, 7 Jul 2023 11:28:27 +0300 Subject: [PATCH 02/16] Add abort controller to loader. --- .../src/services/engine.ts | 12 --- .../src/services/fragment-loader.ts | 92 +++++++++---------- 2 files changed, 42 insertions(+), 62 deletions(-) diff --git a/packages/p2p-media-loader-hlsjs/src/services/engine.ts b/packages/p2p-media-loader-hlsjs/src/services/engine.ts index deb1d3cb..99f50ec0 100644 --- a/packages/p2p-media-loader-hlsjs/src/services/engine.ts +++ b/packages/p2p-media-loader-hlsjs/src/services/engine.ts @@ -18,24 +18,12 @@ export class Engine { public initHlsJsEvents(hls: Hls) { hls.on("hlsManifestLoaded" as Events.MANIFEST_LOADED, (event, data) => { - console.log(data.levels); this.segmentManager.processMasterManifest(data); }); - hls.on("hlsLevelLoaded" as Events.LEVEL_LOADING, (event, data) => { - console.log("LEVEL_LOADING", data); - // this.segmentManager.setPlaylist(data); - }); - hls.on("hlsLevelUpdated" as Events.LEVEL_UPDATED, (event, data) => { - console.log("LEVEL_UPDATED", data); this.segmentManager.setPlaylist(data); }); - - hls.on("hlsLevelLoaded" as Events.LEVEL_LOADED, (event, data) => { - console.log("LEVEL_LOADED"); - // this.segmentManager.setPlaylist(data); - }); } private createFragmentLoaderClass() { diff --git a/packages/p2p-media-loader-hlsjs/src/services/fragment-loader.ts b/packages/p2p-media-loader-hlsjs/src/services/fragment-loader.ts index ac62f6fb..f51e1310 100644 --- a/packages/p2p-media-loader-hlsjs/src/services/fragment-loader.ts +++ b/packages/p2p-media-loader-hlsjs/src/services/fragment-loader.ts @@ -11,8 +11,6 @@ import { SegmentManager } from "./segment-mananger"; import { ByteRange, Segment } from "./playlist"; import Debug from "debug"; -let prev: string | undefined; - export class FragmentLoaderBase implements Loader { context!: FragmentLoaderContext; config!: LoaderConfiguration | null; @@ -20,23 +18,14 @@ export class FragmentLoaderBase implements Loader { stats: LoaderStats; defaultLoader: Loader; segmentManager: SegmentManager; + response?: { status: number; data: ArrayBuffer; url: string; ok: boolean }; + abortController: AbortController = new AbortController(); private debug = Debug("hls:fragment-loading"); constructor(config: HlsConfig, segmentManager: SegmentManager) { this.segmentManager = segmentManager; this.defaultLoader = new config.loader(config); this.stats = this.defaultLoader.stats; - // this.stats = { - // loading: { start: 0, end: 0, first: 0 }, - // total: 0, - // loaded: 0, - // bwEstimate: 0, - // buffering: { start: 0, end: 0, first: 0 }, - // aborted: false, - // retry: 0, - // parsing: { start: 0, end: 0 }, - // chunkCount: 0, - // }; } async load( @@ -49,7 +38,11 @@ export class FragmentLoaderBase implements Loader { this.callbacks = callbacks; const stats = this.stats; - this.identifyPlaylist(context); + const playlist = this.identifyPlaylist(context); + if (!playlist) { + return this.defaultLoader.load(context, config, callbacks); + } + let byteRange: ByteRange | undefined; const { rangeStart, rangeEnd } = context; if ( @@ -59,34 +52,32 @@ export class FragmentLoaderBase implements Loader { ) { byteRange = { start: rangeStart, end: rangeEnd }; } - const response = await this.fetchSegment(context.url, byteRange); + this.response = await this.fetchSegment(context.url, byteRange); const { loading } = stats; - const loadedBytes = response.data.byteLength; + const loadedBytes = this.response.data.byteLength; loading.first = performance.now(); loading.end = performance.now() + 1; - const { bandwidth, loadingStartTime } = - this.getLoadingStartByTargetBandwidth({ - targetLevelBandwidth: 460560, - aboveLevelBandwidth: 836280, - loadingEndTime: loading.first, - loadedBytes, - }); + const { bandwidth, loadingStartTime } = this.getLoadingStatByTargetBitrate({ + targetLevelBitrate: 1650064, + aboveLevelBitrate: 2749539, + loadingEndTime: loading.first, + loadedBytes, + }); - console.log("bandwidth", bandwidth); loading.start = loadingStartTime; stats.bwEstimate = bandwidth; stats.total = stats.loaded = loadedBytes; callbacks.onSuccess( { - url: response.url, - code: response.status, - data: response.data, + url: this.response.url, + code: this.response.status, + data: this.response.data, }, this.stats, context, - response + this.response ); } @@ -96,18 +87,8 @@ export class FragmentLoaderBase implements Loader { start, end, }); - const prevPlaylist = prev - ? this.segmentManager.getPlaylistBySegmentId(prev) - : undefined; const playlist = this.segmentManager.getPlaylistBySegmentId(segmentId); - prev = segmentId; - console.log(""); - console.log("PREV_PLAYLIST", prevPlaylist?.index); - console.log(context.url); - // console.log("SEGMENT_ID: ", segmentId); - console.log("PLAYLIST_INDEX: ", playlist?.index); - console.log("PLAYLIST_TYPE: ", playlist?.type); this.debug( "downloaded segment from playlist\n", `playlist v: ${playlist?.index}\n`, @@ -115,24 +96,23 @@ export class FragmentLoaderBase implements Loader { playlist?.segments.get(segmentId)?.index, `bitrate: ${playlist?.bitrate}` ); + return playlist; } - getLoadingStartByTargetBandwidth({ + getLoadingStatByTargetBitrate({ loadedBytes, - targetLevelBandwidth, - aboveLevelBandwidth, + targetLevelBitrate, + aboveLevelBitrate, loadingEndTime, }: { - targetLevelBandwidth: number; - aboveLevelBandwidth: number; + targetLevelBitrate: number; + aboveLevelBitrate: number; loadingEndTime: number; loadedBytes: number; }) { const bites = loadedBytes * 8; - const levelBandwidthDiff = aboveLevelBandwidth - targetLevelBandwidth; - const targetBandwidth = Math.round( - targetLevelBandwidth + levelBandwidthDiff * 0.4 - ); + const bitrateDiff = aboveLevelBitrate - targetLevelBitrate; + const targetBandwidth = Math.round(targetLevelBitrate + bitrateDiff * 0.4); const timeForLoading = Math.round((bites / targetBandwidth) * 1000); const loadingStartTime = loadingEndTime - timeForLoading; return { loadingStartTime, bandwidth: targetBandwidth }; @@ -146,14 +126,26 @@ export class FragmentLoaderBase implements Loader { const byteRangeString = `bytes=${start}-${end}`; headers.set("Range", byteRangeString); } - const response = await fetch(segmentUrl, { headers }); + const response = await fetch(segmentUrl, { + headers, + signal: this.abortController.signal, + }); const data = await response.arrayBuffer(); - const { status, url } = response; + const { status, url, ok } = response; + + return { status, data, url, ok }; + } - return { status, data, url }; + private abortInternal() { + if (!this.response?.ok) { + this.abortController.abort(); + this.stats.aborted = true; + } } abort() { + this.abortInternal(); + this.callbacks?.onAbort?.(this.stats, this.context, {}); this.defaultLoader.abort(); } From 0cc0d1afd66eae9b43a2ba61f44325533077a16a Mon Sep 17 00:00:00 2001 From: Igor Zolotarenko Date: Fri, 7 Jul 2023 11:29:47 +0300 Subject: [PATCH 03/16] Add loader destroy logic. --- .../p2p-media-loader-hlsjs/src/services/fragment-loader.ts | 3 +++ 1 file changed, 3 insertions(+) diff --git a/packages/p2p-media-loader-hlsjs/src/services/fragment-loader.ts b/packages/p2p-media-loader-hlsjs/src/services/fragment-loader.ts index f51e1310..01c2a86f 100644 --- a/packages/p2p-media-loader-hlsjs/src/services/fragment-loader.ts +++ b/packages/p2p-media-loader-hlsjs/src/services/fragment-loader.ts @@ -151,5 +151,8 @@ export class FragmentLoaderBase implements Loader { destroy() { this.defaultLoader.destroy(); + this.abortInternal(); + this.callbacks = null; + this.config = null; } } From 0205dc990654a2e06eb557d483ec3bd212e87528 Mon Sep 17 00:00:00 2001 From: Igor Zolotarenko Date: Fri, 7 Jul 2023 11:56:10 +0300 Subject: [PATCH 04/16] Add error handling. --- .../src/services/fragment-loader.ts | 13 ++++++++++++- 1 file changed, 12 insertions(+), 1 deletion(-) diff --git a/packages/p2p-media-loader-hlsjs/src/services/fragment-loader.ts b/packages/p2p-media-loader-hlsjs/src/services/fragment-loader.ts index 01c2a86f..0b1a8f6f 100644 --- a/packages/p2p-media-loader-hlsjs/src/services/fragment-loader.ts +++ b/packages/p2p-media-loader-hlsjs/src/services/fragment-loader.ts @@ -52,7 +52,14 @@ export class FragmentLoaderBase implements Loader { ) { byteRange = { start: rangeStart, end: rangeEnd }; } - this.response = await this.fetchSegment(context.url, byteRange); + try { + this.response = await this.fetchSegment(context.url, byteRange); + } catch (error) { + if (!this.stats.aborted) { + return this.handleError(error as { code: number; text: string }); + } + } + if (!this.response) return; const { loading } = stats; const loadedBytes = this.response.data.byteLength; loading.first = performance.now(); @@ -143,6 +150,10 @@ export class FragmentLoaderBase implements Loader { } } + private handleError(error: { code: number; text: string }) { + this.callbacks?.onError(error, this.context, undefined, this.stats); + } + abort() { this.abortInternal(); this.callbacks?.onAbort?.(this.stats, this.context, {}); From dba322e4805c249bb30fc95efd50ecd96d2f0e2d Mon Sep 17 00:00:00 2001 From: Igor Zolotarenko Date: Fri, 7 Jul 2023 18:31:41 +0300 Subject: [PATCH 05/16] Remove comments. Fix lint warnings --- .../src/services/segment-mananger.ts | 36 +------------------ 1 file changed, 1 insertion(+), 35 deletions(-) diff --git a/packages/p2p-media-loader-hlsjs/src/services/segment-mananger.ts b/packages/p2p-media-loader-hlsjs/src/services/segment-mananger.ts index 598528c1..1398f40b 100644 --- a/packages/p2p-media-loader-hlsjs/src/services/segment-mananger.ts +++ b/packages/p2p-media-loader-hlsjs/src/services/segment-mananger.ts @@ -1,9 +1,5 @@ import { Playlist, Segment } from "./playlist"; -import type { - LevelLoadedData, - ManifestLoadedData, - LevelUpdatedData, -} from "hls.js"; +import type { ManifestLoadedData, LevelUpdatedData } from "hls.js"; export class SegmentManager { isLive?: boolean; @@ -44,36 +40,6 @@ export class SegmentManager { }); } - // setPlaylist(data: LevelLoadedData) { - // const { - // details: { url, fragments, live }, - // } = data; - // const playlist = this.playlists.get(url); - // if (!playlist) return; - // - // this.isLive = live; - // - // const segmentToRemoveIds = new Set(playlist.segments.keys()); - // fragments.forEach((fragment, index) => { - // const { url, byteRange, sn } = fragment; - // if (sn === "initSegment") return; - // - // const [start, end] = byteRange; - // const segmentLocalId = Segment.getSegmentLocalId(url, { start, end }); - // segmentToRemoveIds.delete(segmentLocalId); - // - // if (playlist.segments.has(segmentLocalId)) return; - // const segment = new Segment({ - // segmentUrl: url, - // index: live ? sn : index, - // ...(start && end ? { byteRange: { start, end } } : {}), - // }); - // playlist.segments.set(segment.localId, segment); - // }); - // - // segmentToRemoveIds.forEach((value) => playlist.segments.delete(value)); - // } - setPlaylist(data: LevelUpdatedData) { const { details: { url, fragments, live }, From 278795903378b3c2f7f51fc621819f79c190b1e8 Mon Sep 17 00:00:00 2001 From: Igor Zolotarenko Date: Mon, 10 Jul 2023 12:55:48 +0300 Subject: [PATCH 06/16] Create a default loader only if necessary --- p2p-media-loader-demo/src/App.tsx | 14 +++++----- .../src/services/fragment-loader.ts | 27 +++++++++++++------ .../src/services/playlist.ts | 4 --- .../src/services/segment-mananger.ts | 2 -- 4 files changed, 26 insertions(+), 21 deletions(-) diff --git a/p2p-media-loader-demo/src/App.tsx b/p2p-media-loader-demo/src/App.tsx index c3c46d60..6bbf2522 100644 --- a/p2p-media-loader-demo/src/App.tsx +++ b/p2p-media-loader-demo/src/App.tsx @@ -18,7 +18,7 @@ type Player = (typeof players)[number]; type ShakaPlayer = shaka.Player; type ExtendedWindow = Window & { videoPlayer?: { destroy?: () => void } }; -const videoUrl = { +const streamUrl = { bigBunnyBuck: "https://test-streams.mux.dev/x36xhzz/x36xhzz.m3u8", byteRangeVideo: "https://devstreaming-cdn.apple.com/videos/streaming/examples/bipbop_16x9/bipbop_16x9_variant.m3u8", @@ -48,7 +48,7 @@ function App() { const [playerType, setPlayerType] = useState( localStorage.player ); - const [url, setUrl] = useState(localStorage.videoUrl); + const [url, setUrl] = useState(localStorage.streamUrl); const shakaEngine = useRef(new ShakaEngine(shakaLib)); const hlsEngine = useRef(new HlsJsEngine()); const containerRef = useRef(null); @@ -65,9 +65,9 @@ function App() { localStorage.player = "hls-dplayer"; setPlayerType("hls-dplayer"); } - if (!localStorage.videoUrl) { - localStorage.videoUrl = videoUrl.live2; - setUrl(videoUrl.live2); + if (!localStorage.streamUrl) { + localStorage.streamUrl = streamUrl.live2; + setUrl(streamUrl.live2); } switch (playerType) { @@ -182,7 +182,7 @@ function App() { }; const onVideoUrlChange = (url: string) => { - localStorage.videoUrl = url; + localStorage.streamUrl = url; setUrl(url); destroyAndWindowPlayer(); }; @@ -210,7 +210,7 @@ function App() { value={url} onChange={(event) => onVideoUrlChange(event.target.value)} > - {Object.entries(videoUrl).map(([name, url]) => { + {Object.entries(streamUrl).map(([name, url]) => { return (